4 * Side-channel API code for CUPS.
6 * Copyright 2007-2012 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"
32 #include "cups-private.h"
39 # include <sys/time.h>
41 # include <sys/select.h>
44 # include <sys/time.h>
48 #endif /* HAVE_POLL */
52 * Buffer size for side-channel requests...
55 #define _CUPS_SC_MAX_DATA 65535
56 #define _CUPS_SC_MAX_BUFFER 65540
60 * 'cupsSideChannelDoRequest()' - Send a side-channel command to a backend and wait for a response.
62 * This function is normally only called by filters, drivers, or port
63 * monitors in order to communicate with the backend used by the current
64 * printer. Programs must be prepared to handle timeout or "not
65 * implemented" status codes, which indicate that the backend or device
66 * do not support the specified side-channel command.
68 * The "datalen" parameter must be initialized to the size of the buffer
69 * pointed to by the "data" parameter. cupsSideChannelDoRequest() will
70 * update the value to contain the number of data bytes in the buffer.
72 * @since CUPS 1.3/OS X 10.5@
75 cups_sc_status_t
/* O - Status of command */
76 cupsSideChannelDoRequest(
77 cups_sc_command_t command
, /* I - Command to send */
78 char *data
, /* O - Response data buffer pointer */
79 int *datalen
, /* IO - Size of data buffer on entry, number of bytes in buffer on return */
80 double timeout
) /* I - Timeout in seconds */
82 cups_sc_status_t status
; /* Status of command */
83 cups_sc_command_t rcommand
; /* Response command */
86 if (cupsSideChannelWrite(command
, CUPS_SC_STATUS_NONE
, NULL
, 0, timeout
))
87 return (CUPS_SC_STATUS_TIMEOUT
);
89 if (cupsSideChannelRead(&rcommand
, &status
, data
, datalen
, timeout
))
90 return (CUPS_SC_STATUS_TIMEOUT
);
92 if (rcommand
!= command
)
93 return (CUPS_SC_STATUS_BAD_MESSAGE
);
100 * 'cupsSideChannelRead()' - Read a side-channel message.
102 * This function is normally only called by backend programs to read
103 * commands from a filter, driver, or port monitor program. The
104 * caller must be prepared to handle incomplete or invalid messages
105 * and return the corresponding status codes.
107 * The "datalen" parameter must be initialized to the size of the buffer
108 * pointed to by the "data" parameter. cupsSideChannelDoRequest() will
109 * update the value to contain the number of data bytes in the buffer.
111 * @since CUPS 1.3/OS X 10.5@
114 int /* O - 0 on success, -1 on error */
116 cups_sc_command_t
*command
, /* O - Command code */
117 cups_sc_status_t
*status
, /* O - Status code */
118 char *data
, /* O - Data buffer pointer */
119 int *datalen
, /* IO - Size of data buffer on entry, number of bytes in buffer on return */
120 double timeout
) /* I - Timeout in seconds */
122 char *buffer
; /* Message buffer */
123 int bytes
; /* Bytes read */
124 int templen
; /* Data length from message */
125 int nfds
; /* Number of file descriptors */
127 struct pollfd pfd
; /* Poll structure for poll() */
129 fd_set input_set
; /* Input set for select() */
130 struct timeval stimeout
; /* Timeout value for select() */
131 #endif /* HAVE_POLL */
134 DEBUG_printf(("cupsSideChannelRead(command=%p, status=%p, data=%p, "
135 "datalen=%p(%d), timeout=%.3f)", command
, status
, data
,
136 datalen
, datalen
? *datalen
: -1, timeout
));
139 * Range check input...
142 if (!command
|| !status
)
146 * See if we have pending data on the side-channel socket...
153 while ((nfds
= poll(&pfd
, 1,
154 timeout
< 0.0 ? -1 : (long)(timeout
* 1000))) < 0 &&
155 (errno
== EINTR
|| errno
== EAGAIN
))
160 FD_SET(CUPS_SC_FD
, &input_set
);
162 stimeout
.tv_sec
= (int)timeout
;
163 stimeout
.tv_usec
= (int)(timeout
* 1000000) % 1000000;
165 while ((nfds
= select(CUPS_SC_FD
+ 1, &input_set
, NULL
, NULL
,
166 timeout
< 0.0 ? NULL
: &stimeout
)) < 0 &&
167 (errno
== EINTR
|| errno
== EAGAIN
))
170 #endif /* HAVE_POLL */
174 *command
= CUPS_SC_CMD_NONE
;
175 *status
= nfds
==0 ? CUPS_SC_STATUS_TIMEOUT
: CUPS_SC_STATUS_IO_ERROR
;
180 * Read a side-channel message for the format:
182 * Byte(s) Description
183 * ------- -------------------------------------------
186 * 2-3 Data length (network byte order)
190 if ((buffer
= _cupsBufferGet(_CUPS_SC_MAX_BUFFER
)) == NULL
)
192 *command
= CUPS_SC_CMD_NONE
;
193 *status
= CUPS_SC_STATUS_TOO_BIG
;
198 while ((bytes
= read(CUPS_SC_FD
, buffer
, _CUPS_SC_MAX_BUFFER
)) < 0)
199 if (errno
!= EINTR
&& errno
!= EAGAIN
)
201 DEBUG_printf(("1cupsSideChannelRead: Read error: %s", strerror(errno
)));
203 _cupsBufferRelease(buffer
);
205 *command
= CUPS_SC_CMD_NONE
;
206 *status
= CUPS_SC_STATUS_IO_ERROR
;
212 * Watch for EOF or too few bytes...
217 DEBUG_printf(("1cupsSideChannelRead: Short read of %d bytes", bytes
));
219 _cupsBufferRelease(buffer
);
221 *command
= CUPS_SC_CMD_NONE
;
222 *status
= CUPS_SC_STATUS_BAD_MESSAGE
;
228 * Validate the command code in the message...
231 if (buffer
[0] < CUPS_SC_CMD_SOFT_RESET
||
232 buffer
[0] >= CUPS_SC_CMD_MAX
)
234 DEBUG_printf(("1cupsSideChannelRead: Bad command %d!", buffer
[0]));
236 _cupsBufferRelease(buffer
);
238 *command
= CUPS_SC_CMD_NONE
;
239 *status
= CUPS_SC_STATUS_BAD_MESSAGE
;
244 *command
= (cups_sc_command_t
)buffer
[0];
247 * Validate the data length in the message...
250 templen
= ((buffer
[2] & 255) << 8) | (buffer
[3] & 255);
252 if (templen
> 0 && (!data
|| !datalen
))
255 * Either the response is bigger than the provided buffer or the
256 * response is bigger than we've read...
259 *status
= CUPS_SC_STATUS_TOO_BIG
;
261 else if (!datalen
|| templen
> *datalen
|| templen
> (bytes
- 4))
264 * Either the response is bigger than the provided buffer or the
265 * response is bigger than we've read...
268 *status
= CUPS_SC_STATUS_TOO_BIG
;
273 * The response data will fit, copy it over and provide the actual
277 *status
= (cups_sc_status_t
)buffer
[1];
280 memcpy(data
, buffer
+ 4, templen
);
283 _cupsBufferRelease(buffer
);
285 DEBUG_printf(("1cupsSideChannelRead: Returning status=%d", *status
));
292 * 'cupsSideChannelSNMPGet()' - Query a SNMP OID's value.
294 * This function asks the backend to do a SNMP OID query on behalf of the
295 * filter, port monitor, or backend using the default community name.
297 * "oid" contains a numeric OID consisting of integers separated by periods,
298 * for example ".1.3.6.1.2.1.43". Symbolic names from SNMP MIBs are not
299 * supported and must be converted to their numeric forms.
301 * On input, "data" and "datalen" provide the location and size of the
302 * buffer to hold the OID value as a string. HEX-String (binary) values are
303 * converted to hexadecimal strings representing the binary data, while
304 * NULL-Value and unknown OID types are returned as the empty string.
305 * The returned "datalen" does not include the trailing nul.
307 * @code CUPS_SC_STATUS_NOT_IMPLEMENTED@ is returned by backends that do not
308 * support SNMP queries. @code CUPS_SC_STATUS_NO_RESPONSE@ is returned when
309 * the printer does not respond to the SNMP query.
311 * @since CUPS 1.4/OS X 10.6@
314 cups_sc_status_t
/* O - Query status */
315 cupsSideChannelSNMPGet(
316 const char *oid
, /* I - OID to query */
317 char *data
, /* I - Buffer for OID value */
318 int *datalen
, /* IO - Size of OID buffer on entry, size of value on return */
319 double timeout
) /* I - Timeout in seconds */
321 cups_sc_status_t status
; /* Status of command */
322 cups_sc_command_t rcommand
; /* Response command */
323 char *real_data
; /* Real data buffer for response */
324 int real_datalen
, /* Real length of data buffer */
325 real_oidlen
; /* Length of returned OID string */
328 DEBUG_printf(("cupsSideChannelSNMPGet(oid=\"%s\", data=%p, datalen=%p(%d), "
329 "timeout=%.3f)", oid
, data
, datalen
, datalen
? *datalen
: -1,
333 * Range check input...
336 if (!oid
|| !*oid
|| !data
|| !datalen
|| *datalen
< 2)
337 return (CUPS_SC_STATUS_BAD_MESSAGE
);
342 * Send the request to the backend and wait for a response...
345 if (cupsSideChannelWrite(CUPS_SC_CMD_SNMP_GET
, CUPS_SC_STATUS_NONE
, oid
,
346 (int)strlen(oid
) + 1, timeout
))
347 return (CUPS_SC_STATUS_TIMEOUT
);
349 if ((real_data
= _cupsBufferGet(_CUPS_SC_MAX_BUFFER
)) == NULL
)
350 return (CUPS_SC_STATUS_TOO_BIG
);
352 real_datalen
= _CUPS_SC_MAX_BUFFER
;
353 if (cupsSideChannelRead(&rcommand
, &status
, real_data
, &real_datalen
, timeout
))
355 _cupsBufferRelease(real_data
);
356 return (CUPS_SC_STATUS_TIMEOUT
);
359 if (rcommand
!= CUPS_SC_CMD_SNMP_GET
)
361 _cupsBufferRelease(real_data
);
362 return (CUPS_SC_STATUS_BAD_MESSAGE
);
365 if (status
== CUPS_SC_STATUS_OK
)
368 * Parse the response of the form "oid\0value"...
371 real_oidlen
= strlen(real_data
) + 1;
372 real_datalen
-= real_oidlen
;
374 if ((real_datalen
+ 1) > *datalen
)
376 _cupsBufferRelease(real_data
);
377 return (CUPS_SC_STATUS_TOO_BIG
);
380 memcpy(data
, real_data
+ real_oidlen
, real_datalen
);
381 data
[real_datalen
] = '\0';
383 *datalen
= real_datalen
;
386 _cupsBufferRelease(real_data
);
393 * 'cupsSideChannelSNMPWalk()' - Query multiple SNMP OID values.
395 * This function asks the backend to do multiple SNMP OID queries on behalf
396 * of the filter, port monitor, or backend using the default community name.
397 * All OIDs under the "parent" OID are queried and the results are sent to
398 * the callback function you provide.
400 * "oid" contains a numeric OID consisting of integers separated by periods,
401 * for example ".1.3.6.1.2.1.43". Symbolic names from SNMP MIBs are not
402 * supported and must be converted to their numeric forms.
404 * "timeout" specifies the timeout for each OID query. The total amount of
405 * time will depend on the number of OID values found and the time required
408 * "cb" provides a function to call for every value that is found. "context"
409 * is an application-defined pointer that is sent to the callback function
410 * along with the OID and current data. The data passed to the callback is the
411 * same as returned by @link cupsSideChannelSNMPGet@.
413 * @code CUPS_SC_STATUS_NOT_IMPLEMENTED@ is returned by backends that do not
414 * support SNMP queries. @code CUPS_SC_STATUS_NO_RESPONSE@ is returned when
415 * the printer does not respond to the first SNMP query.
417 * @since CUPS 1.4/OS X 10.6@
420 cups_sc_status_t
/* O - Status of first query of @code CUPS_SC_STATUS_OK@ on success */
421 cupsSideChannelSNMPWalk(
422 const char *oid
, /* I - First numeric OID to query */
423 double timeout
, /* I - Timeout for each query in seconds */
424 cups_sc_walk_func_t cb
, /* I - Function to call with each value */
425 void *context
) /* I - Application-defined pointer to send to callback */
427 cups_sc_status_t status
; /* Status of command */
428 cups_sc_command_t rcommand
; /* Response command */
429 char *real_data
; /* Real data buffer for response */
430 int real_datalen
, /* Real length of data buffer */
431 real_oidlen
, /* Length of returned OID string */
432 oidlen
; /* Length of first OID */
433 const char *current_oid
; /* Current OID */
434 char last_oid
[2048]; /* Last OID */
437 DEBUG_printf(("cupsSideChannelSNMPWalk(oid=\"%s\", timeout=%.3f, cb=%p, "
438 "context=%p)", oid
, timeout
, cb
, context
));
441 * Range check input...
444 if (!oid
|| !*oid
|| !cb
)
445 return (CUPS_SC_STATUS_BAD_MESSAGE
);
447 if ((real_data
= _cupsBufferGet(_CUPS_SC_MAX_BUFFER
)) == NULL
)
448 return (CUPS_SC_STATUS_TOO_BIG
);
451 * Loop until the OIDs don't match...
455 oidlen
= (int)strlen(oid
);
461 * Send the request to the backend and wait for a response...
464 if (cupsSideChannelWrite(CUPS_SC_CMD_SNMP_GET_NEXT
, CUPS_SC_STATUS_NONE
,
465 current_oid
, (int)strlen(current_oid
) + 1, timeout
))
467 _cupsBufferRelease(real_data
);
468 return (CUPS_SC_STATUS_TIMEOUT
);
471 real_datalen
= _CUPS_SC_MAX_BUFFER
;
472 if (cupsSideChannelRead(&rcommand
, &status
, real_data
, &real_datalen
,
475 _cupsBufferRelease(real_data
);
476 return (CUPS_SC_STATUS_TIMEOUT
);
479 if (rcommand
!= CUPS_SC_CMD_SNMP_GET_NEXT
)
481 _cupsBufferRelease(real_data
);
482 return (CUPS_SC_STATUS_BAD_MESSAGE
);
485 if (status
== CUPS_SC_STATUS_OK
)
488 * Parse the response of the form "oid\0value"...
491 if (strncmp(real_data
, oid
, oidlen
) || real_data
[oidlen
] != '.' ||
492 !strcmp(real_data
, last_oid
))
495 * Done with this set of OIDs...
498 _cupsBufferRelease(real_data
);
499 return (CUPS_SC_STATUS_OK
);
502 if (real_datalen
< sizeof(real_data
))
503 real_data
[real_datalen
] = '\0';
505 real_oidlen
= strlen(real_data
) + 1;
506 real_datalen
-= real_oidlen
;
509 * Call the callback with the OID and data...
512 (*cb
)(real_data
, real_data
+ real_oidlen
, real_datalen
, context
);
515 * Update the current OID...
518 current_oid
= real_data
;
519 strlcpy(last_oid
, current_oid
, sizeof(last_oid
));
522 while (status
== CUPS_SC_STATUS_OK
);
524 _cupsBufferRelease(real_data
);
531 * 'cupsSideChannelWrite()' - Write a side-channel message.
533 * This function is normally only called by backend programs to send
534 * responses to a filter, driver, or port monitor program.
536 * @since CUPS 1.3/OS X 10.5@
539 int /* O - 0 on success, -1 on error */
540 cupsSideChannelWrite(
541 cups_sc_command_t command
, /* I - Command code */
542 cups_sc_status_t status
, /* I - Status code */
543 const char *data
, /* I - Data buffer pointer */
544 int datalen
, /* I - Number of bytes of data */
545 double timeout
) /* I - Timeout in seconds */
547 char *buffer
; /* Message buffer */
548 int bytes
; /* Bytes written */
550 struct pollfd pfd
; /* Poll structure for poll() */
552 fd_set output_set
; /* Output set for select() */
553 struct timeval stimeout
; /* Timeout value for select() */
554 #endif /* HAVE_POLL */
558 * Range check input...
561 if (command
< CUPS_SC_CMD_SOFT_RESET
|| command
>= CUPS_SC_CMD_MAX
||
562 datalen
< 0 || datalen
> _CUPS_SC_MAX_DATA
|| (datalen
> 0 && !data
))
566 * See if we can safely write to the side-channel socket...
571 pfd
.events
= POLLOUT
;
575 if (poll(&pfd
, 1, -1) < 1)
578 else if (poll(&pfd
, 1, (long)(timeout
* 1000)) < 1)
582 FD_ZERO(&output_set
);
583 FD_SET(CUPS_SC_FD
, &output_set
);
587 if (select(CUPS_SC_FD
+ 1, NULL
, &output_set
, NULL
, NULL
) < 1)
592 stimeout
.tv_sec
= (int)timeout
;
593 stimeout
.tv_usec
= (int)(timeout
* 1000000) % 1000000;
595 if (select(CUPS_SC_FD
+ 1, NULL
, &output_set
, NULL
, &stimeout
) < 1)
598 #endif /* HAVE_POLL */
601 * Write a side-channel message in the format:
603 * Byte(s) Description
604 * ------- -------------------------------------------
607 * 2-3 Data length (network byte order) <= 16384
611 if ((buffer
= _cupsBufferGet(datalen
+ 4)) == NULL
)
616 buffer
[2] = datalen
>> 8;
617 buffer
[3] = datalen
& 255;
623 memcpy(buffer
+ 4, data
, datalen
);
627 while (write(CUPS_SC_FD
, buffer
, bytes
) < 0)
628 if (errno
!= EINTR
&& errno
!= EAGAIN
)
630 _cupsBufferRelease(buffer
);
634 _cupsBufferRelease(buffer
);