2 * "$Id: sidechannel.c 7720 2008-07-11 22:46:21Z mike $"
4 * Side-channel API code for the Common UNIX Printing System (CUPS).
6 * Copyright 2007-2009 by Apple Inc.
7 * Copyright 2006 by Easy Software Products.
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
15 * This file is subject to the Apple OS-Developed Software exception.
19 * cupsSideChannelDoRequest() - Send a side-channel command to a backend and
20 * wait for a response.
21 * cupsSideChannelRead() - Read a side-channel message.
22 * cupsSideChannelSNMPGet() - Query a SNMP OID's value.
23 * cupsSideChannelSNMPWalk() - Query multiple SNMP OID values.
24 * cupsSideChannelWrite() - Write a side-channel message.
28 * Include necessary headers...
31 #include "sidechannel.h"
37 # include <sys/time.h>
39 # include <sys/select.h>
42 # include <sys/time.h>
45 # include <sys/poll.h>
46 #endif /* HAVE_POLL */
50 * 'cupsSideChannelDoRequest()' - Send a side-channel command to a backend and wait for a response.
52 * This function is normally only called by filters, drivers, or port
53 * monitors in order to communicate with the backend used by the current
54 * printer. Programs must be prepared to handle timeout or "not
55 * implemented" status codes, which indicate that the backend or device
56 * do not support the specified side-channel command.
58 * The "datalen" parameter must be initialized to the size of the buffer
59 * pointed to by the "data" parameter. cupsSideChannelDoRequest() will
60 * update the value to contain the number of data bytes in the buffer.
62 * @since CUPS 1.3/Mac OS X 10.5@
65 cups_sc_status_t
/* O - Status of command */
66 cupsSideChannelDoRequest(
67 cups_sc_command_t command
, /* I - Command to send */
68 char *data
, /* O - Response data buffer pointer */
69 int *datalen
, /* IO - Size of data buffer on entry, number of bytes in buffer on return */
70 double timeout
) /* I - Timeout in seconds */
72 cups_sc_status_t status
; /* Status of command */
73 cups_sc_command_t rcommand
; /* Response command */
76 if (cupsSideChannelWrite(command
, CUPS_SC_STATUS_NONE
, NULL
, 0, timeout
))
77 return (CUPS_SC_STATUS_TIMEOUT
);
79 if (cupsSideChannelRead(&rcommand
, &status
, data
, datalen
, timeout
))
80 return (CUPS_SC_STATUS_TIMEOUT
);
82 if (rcommand
!= command
)
83 return (CUPS_SC_STATUS_BAD_MESSAGE
);
90 * 'cupsSideChannelRead()' - Read a side-channel message.
92 * This function is normally only called by backend programs to read
93 * commands from a filter, driver, or port monitor program. The
94 * caller must be prepared to handle incomplete or invalid messages
95 * and return the corresponding status codes.
97 * The "datalen" parameter must be initialized to the size of the buffer
98 * pointed to by the "data" parameter. cupsSideChannelDoRequest() will
99 * update the value to contain the number of data bytes in the buffer.
101 * @since CUPS 1.3/Mac OS X 10.5@
104 int /* O - 0 on success, -1 on error */
106 cups_sc_command_t
*command
, /* O - Command code */
107 cups_sc_status_t
*status
, /* O - Status code */
108 char *data
, /* O - Data buffer pointer */
109 int *datalen
, /* IO - Size of data buffer on entry, number of bytes in buffer on return */
110 double timeout
) /* I - Timeout in seconds */
112 char buffer
[16388]; /* Message buffer */
113 int bytes
; /* Bytes read */
114 int templen
; /* Data length from message */
116 struct pollfd pfd
; /* Poll structure for poll() */
118 fd_set input_set
; /* Input set for select() */
119 struct timeval stimeout
; /* Timeout value for select() */
120 #endif /* HAVE_POLL */
123 DEBUG_printf(("cupsSideChannelRead(command=%p, status=%p, data=%p, "
124 "datalen=%p(%d), timeout=%.3f)", command
, status
, data
,
125 datalen
, datalen
? *datalen
: -1, timeout
));
128 * Range check input...
131 if (!command
|| !status
)
135 * See if we have pending data on the side-channel socket...
144 if (poll(&pfd
, 1, -1) < 1)
147 else if (poll(&pfd
, 1, (long)(timeout
* 1000)) < 1)
152 FD_SET(CUPS_SC_FD
, &input_set
);
156 if (select(CUPS_SC_FD
+ 1, &input_set
, NULL
, NULL
, NULL
) < 1)
158 DEBUG_printf(("1cupsSideChannelRead: Select error: %s", strerror(errno
)));
164 stimeout
.tv_sec
= (int)timeout
;
165 stimeout
.tv_usec
= (int)(timeout
* 1000000) % 1000000;
167 if (select(CUPS_SC_FD
+ 1, &input_set
, NULL
, NULL
, &stimeout
) < 1)
169 DEBUG_puts("1cupsSideChannelRead: Select timeout");
173 #endif /* HAVE_POLL */
176 * Read a side-channel message for the format:
178 * Byte(s) Description
179 * ------- -------------------------------------------
182 * 2-3 Data length (network byte order) <= 16384
186 while ((bytes
= read(CUPS_SC_FD
, buffer
, sizeof(buffer
))) < 0)
187 if (errno
!= EINTR
&& errno
!= EAGAIN
)
189 DEBUG_printf(("1cupsSideChannelRead: Read error: %s", strerror(errno
)));
194 * Validate the command code in the message...
197 if (buffer
[0] < CUPS_SC_CMD_SOFT_RESET
||
198 buffer
[0] > CUPS_SC_CMD_SNMP_GET_NEXT
)
200 DEBUG_printf(("1cupsSideChannelRead: Bad command %d!", buffer
[0]));
204 *command
= (cups_sc_command_t
)buffer
[0];
207 * Validate the data length in the message...
210 templen
= ((buffer
[2] & 255) << 8) | (buffer
[3] & 255);
212 if (templen
> 0 && (!data
|| !datalen
))
215 * Either the response is bigger than the provided buffer or the
216 * response is bigger than we've read...
219 *status
= CUPS_SC_STATUS_TOO_BIG
;
221 else if (!datalen
|| templen
> *datalen
|| templen
> (bytes
- 4))
224 * Either the response is bigger than the provided buffer or the
225 * response is bigger than we've read...
228 *status
= CUPS_SC_STATUS_TOO_BIG
;
233 * The response data will fit, copy it over and provide the actual
237 *status
= (cups_sc_status_t
)buffer
[1];
240 memcpy(data
, buffer
+ 4, templen
);
243 DEBUG_printf(("1cupsSideChannelRead: Returning status=%d", *status
));
250 * 'cupsSideChannelSNMPGet()' - Query a SNMP OID's value.
252 * This function asks the backend to do a SNMP OID query on behalf of the
253 * filter, port monitor, or backend using the default community name.
255 * "oid" contains a numeric OID consisting of integers separated by periods,
256 * for example ".1.3.6.1.2.1.43". Symbolic names from SNMP MIBs are not
257 * supported and must be converted to their numeric forms.
259 * On input, "data" and "datalen" provide the location and size of the
260 * buffer to hold the OID value as a string. HEX-String (binary) values are
261 * converted to hexadecimal strings representing the binary data, while
262 * NULL-Value and unknown OID types are returned as the empty string.
263 * The returned "datalen" does not include the trailing nul.
265 * @code CUPS_SC_STATUS_NOT_IMPLEMENTED@ is returned by backends that do not
266 * support SNMP queries. @code CUPS_SC_STATUS_NO_RESPONSE@ is returned when
267 * the printer does not respond to the SNMP query.
269 * @since CUPS 1.4/Mac OS X 10.6@
272 cups_sc_status_t
/* O - Query status */
273 cupsSideChannelSNMPGet(
274 const char *oid
, /* I - OID to query */
275 char *data
, /* I - Buffer for OID value */
276 int *datalen
, /* IO - Size of OID buffer on entry, size of value on return */
277 double timeout
) /* I - Timeout in seconds */
279 cups_sc_status_t status
; /* Status of command */
280 cups_sc_command_t rcommand
; /* Response command */
281 char real_data
[2048];/* Real data buffer for response */
282 int real_datalen
, /* Real length of data buffer */
283 real_oidlen
; /* Length of returned OID string */
286 DEBUG_printf(("cupsSideChannelSNMPGet(oid=\"%s\", data=%p, datalen=%p(%d), "
287 "timeout=%.3f)", oid
, data
, datalen
, datalen
? *datalen
: -1,
291 * Range check input...
294 if (!oid
|| !*oid
|| !data
|| !datalen
|| *datalen
< 2)
295 return (CUPS_SC_STATUS_BAD_MESSAGE
);
300 * Send the request to the backend and wait for a response...
303 if (cupsSideChannelWrite(CUPS_SC_CMD_SNMP_GET
, CUPS_SC_STATUS_NONE
, oid
,
304 (int)strlen(oid
) + 1, timeout
))
305 return (CUPS_SC_STATUS_TIMEOUT
);
307 real_datalen
= sizeof(real_data
);
308 if (cupsSideChannelRead(&rcommand
, &status
, real_data
, &real_datalen
, timeout
))
309 return (CUPS_SC_STATUS_TIMEOUT
);
311 if (rcommand
!= CUPS_SC_CMD_SNMP_GET
)
312 return (CUPS_SC_STATUS_BAD_MESSAGE
);
314 if (status
== CUPS_SC_STATUS_OK
)
317 * Parse the response of the form "oid\0value"...
320 real_oidlen
= strlen(real_data
) + 1;
321 real_datalen
-= real_oidlen
;
323 if ((real_datalen
+ 1) > *datalen
)
324 return (CUPS_SC_STATUS_TOO_BIG
);
326 memcpy(data
, real_data
+ real_oidlen
, real_datalen
);
327 data
[real_datalen
] = '\0';
329 *datalen
= real_datalen
;
337 * 'cupsSideChannelSNMPWalk()' - Query multiple SNMP OID values.
339 * This function asks the backend to do multiple SNMP OID queries on behalf
340 * of the filter, port monitor, or backend using the default community name.
341 * All OIDs under the "parent" OID are queried and the results are sent to
342 * the callback function you provide.
344 * "oid" contains a numeric OID consisting of integers separated by periods,
345 * for example ".1.3.6.1.2.1.43". Symbolic names from SNMP MIBs are not
346 * supported and must be converted to their numeric forms.
348 * "timeout" specifies the timeout for each OID query. The total amount of
349 * time will depend on the number of OID values found and the time required
352 * "cb" provides a function to call for every value that is found. "context"
353 * is an application-defined pointer that is sent to the callback function
354 * along with the OID and current data. The data passed to the callback is the
355 * same as returned by @link cupsSideChannelSNMPGet@.
357 * @code CUPS_SC_STATUS_NOT_IMPLEMENTED@ is returned by backends that do not
358 * support SNMP queries. @code CUPS_SC_STATUS_NO_RESPONSE@ is returned when
359 * the printer does not respond to the first SNMP query.
361 * @since CUPS 1.4/Mac OS X 10.6@
364 cups_sc_status_t
/* O - Status of first query of @code CUPS_SC_STATUS_OK@ on success */
365 cupsSideChannelSNMPWalk(
366 const char *oid
, /* I - First numeric OID to query */
367 double timeout
, /* I - Timeout for each query in seconds */
368 cups_sc_walk_func_t cb
, /* I - Function to call with each value */
369 void *context
) /* I - Application-defined pointer to send to callback */
371 cups_sc_status_t status
; /* Status of command */
372 cups_sc_command_t rcommand
; /* Response command */
373 char real_data
[2048];/* Real data buffer for response */
374 int real_datalen
, /* Real length of data buffer */
375 real_oidlen
, /* Length of returned OID string */
376 oidlen
; /* Length of first OID */
377 const char *current_oid
; /* Current OID */
380 DEBUG_printf(("cupsSideChannelSNMPWalk(oid=\"%s\", timeout=%.3f, cb=%p, "
381 "context=%p)", oid
, timeout
, cb
, context
));
384 * Range check input...
387 if (!oid
|| !*oid
|| !cb
)
388 return (CUPS_SC_STATUS_BAD_MESSAGE
);
391 * Loop until the OIDs don't match...
395 oidlen
= (int)strlen(oid
);
400 * Send the request to the backend and wait for a response...
403 if (cupsSideChannelWrite(CUPS_SC_CMD_SNMP_GET_NEXT
, CUPS_SC_STATUS_NONE
,
404 current_oid
, (int)strlen(current_oid
) + 1, timeout
))
405 return (CUPS_SC_STATUS_TIMEOUT
);
407 real_datalen
= sizeof(real_data
);
408 if (cupsSideChannelRead(&rcommand
, &status
, real_data
, &real_datalen
,
410 return (CUPS_SC_STATUS_TIMEOUT
);
412 if (rcommand
!= CUPS_SC_CMD_SNMP_GET_NEXT
)
413 return (CUPS_SC_STATUS_BAD_MESSAGE
);
415 if (status
== CUPS_SC_STATUS_OK
)
418 * Parse the response of the form "oid\0value"...
421 if (strncmp(real_data
, oid
, oidlen
) || real_data
[oidlen
] != '.')
424 * Done with this set of OIDs...
427 return (CUPS_SC_STATUS_OK
);
430 if (real_datalen
< sizeof(real_data
))
431 real_data
[real_datalen
] = '\0';
433 real_oidlen
= strlen(real_data
) + 1;
434 real_datalen
-= real_oidlen
;
437 * Call the callback with the OID and data...
440 (*cb
)(real_data
, real_data
+ real_oidlen
, real_datalen
, context
);
443 * Update the current OID...
446 current_oid
= real_data
;
449 while (status
== CUPS_SC_STATUS_OK
);
456 * 'cupsSideChannelWrite()' - Write a side-channel message.
458 * This function is normally only called by backend programs to send
459 * responses to a filter, driver, or port monitor program.
461 * @since CUPS 1.3/Mac OS X 10.5@
464 int /* O - 0 on success, -1 on error */
465 cupsSideChannelWrite(
466 cups_sc_command_t command
, /* I - Command code */
467 cups_sc_status_t status
, /* I - Status code */
468 const char *data
, /* I - Data buffer pointer */
469 int datalen
, /* I - Number of bytes of data */
470 double timeout
) /* I - Timeout in seconds */
472 char buffer
[16388]; /* Message buffer */
473 int bytes
; /* Bytes written */
475 struct pollfd pfd
; /* Poll structure for poll() */
477 fd_set output_set
; /* Output set for select() */
478 struct timeval stimeout
; /* Timeout value for select() */
479 #endif /* HAVE_POLL */
483 * Range check input...
486 if (command
< CUPS_SC_CMD_SOFT_RESET
|| command
> CUPS_SC_CMD_SNMP_GET_NEXT
||
487 datalen
< 0 || datalen
> 16384 || (datalen
> 0 && !data
))
491 * See if we can safely write to the side-channel socket...
496 pfd
.events
= POLLOUT
;
500 if (poll(&pfd
, 1, -1) < 1)
503 else if (poll(&pfd
, 1, (long)(timeout
* 1000)) < 1)
507 FD_ZERO(&output_set
);
508 FD_SET(CUPS_SC_FD
, &output_set
);
512 if (select(CUPS_SC_FD
+ 1, NULL
, &output_set
, NULL
, NULL
) < 1)
517 stimeout
.tv_sec
= (int)timeout
;
518 stimeout
.tv_usec
= (int)(timeout
* 1000000) % 1000000;
520 if (select(CUPS_SC_FD
+ 1, NULL
, &output_set
, NULL
, &stimeout
) < 1)
523 #endif /* HAVE_POLL */
526 * Write a side-channel message in the format:
528 * Byte(s) Description
529 * ------- -------------------------------------------
532 * 2-3 Data length (network byte order) <= 16384
538 buffer
[2] = datalen
>> 8;
539 buffer
[3] = datalen
& 255;
545 memcpy(buffer
+ 4, data
, datalen
);
549 while (write(CUPS_SC_FD
, buffer
, bytes
) < 0)
550 if (errno
!= EINTR
&& errno
!= EAGAIN
)
558 * End of "$Id: sidechannel.c 7720 2008-07-11 22:46:21Z mike $".