2 * Side-channel API code for CUPS.
4 * Copyright 2007-2014 by Apple Inc.
5 * Copyright 2006 by Easy Software Products.
7 * Licensed under Apache License v2.0. See the file "LICENSE" for more information.
11 * Include necessary headers...
14 #include "sidechannel.h"
15 #include "cups-private.h"
16 #include "debug-internal.h"
21 # include <sys/select.h>
22 # include <sys/time.h>
26 #endif /* HAVE_POLL */
30 * Buffer size for side-channel requests...
33 #define _CUPS_SC_MAX_DATA 65535
34 #define _CUPS_SC_MAX_BUFFER 65540
38 * 'cupsSideChannelDoRequest()' - Send a side-channel command to a backend and wait for a response.
40 * This function is normally only called by filters, drivers, or port
41 * monitors in order to communicate with the backend used by the current
42 * printer. Programs must be prepared to handle timeout or "not
43 * implemented" status codes, which indicate that the backend or device
44 * do not support the specified side-channel command.
46 * The "datalen" parameter must be initialized to the size of the buffer
47 * pointed to by the "data" parameter. cupsSideChannelDoRequest() will
48 * update the value to contain the number of data bytes in the buffer.
50 * @since CUPS 1.3/macOS 10.5@
53 cups_sc_status_t
/* O - Status of command */
54 cupsSideChannelDoRequest(
55 cups_sc_command_t command
, /* I - Command to send */
56 char *data
, /* O - Response data buffer pointer */
57 int *datalen
, /* IO - Size of data buffer on entry, number of bytes in buffer on return */
58 double timeout
) /* I - Timeout in seconds */
60 cups_sc_status_t status
; /* Status of command */
61 cups_sc_command_t rcommand
; /* Response command */
64 if (cupsSideChannelWrite(command
, CUPS_SC_STATUS_NONE
, NULL
, 0, timeout
))
65 return (CUPS_SC_STATUS_TIMEOUT
);
67 if (cupsSideChannelRead(&rcommand
, &status
, data
, datalen
, timeout
))
68 return (CUPS_SC_STATUS_TIMEOUT
);
70 if (rcommand
!= command
)
71 return (CUPS_SC_STATUS_BAD_MESSAGE
);
78 * 'cupsSideChannelRead()' - Read a side-channel message.
80 * This function is normally only called by backend programs to read
81 * commands from a filter, driver, or port monitor program. The
82 * caller must be prepared to handle incomplete or invalid messages
83 * and return the corresponding status codes.
85 * The "datalen" parameter must be initialized to the size of the buffer
86 * pointed to by the "data" parameter. cupsSideChannelDoRequest() will
87 * update the value to contain the number of data bytes in the buffer.
89 * @since CUPS 1.3/macOS 10.5@
92 int /* O - 0 on success, -1 on error */
94 cups_sc_command_t
*command
, /* O - Command code */
95 cups_sc_status_t
*status
, /* O - Status code */
96 char *data
, /* O - Data buffer pointer */
97 int *datalen
, /* IO - Size of data buffer on entry, number of bytes in buffer on return */
98 double timeout
) /* I - Timeout in seconds */
100 char *buffer
; /* Message buffer */
101 ssize_t bytes
; /* Bytes read */
102 int templen
; /* Data length from message */
103 int nfds
; /* Number of file descriptors */
105 struct pollfd pfd
; /* Poll structure for poll() */
107 fd_set input_set
; /* Input set for select() */
108 struct timeval stimeout
; /* Timeout value for select() */
109 #endif /* HAVE_POLL */
112 DEBUG_printf(("cupsSideChannelRead(command=%p, status=%p, data=%p, "
113 "datalen=%p(%d), timeout=%.3f)", command
, status
, data
,
114 datalen
, datalen
? *datalen
: -1, timeout
));
117 * Range check input...
120 if (!command
|| !status
)
124 * See if we have pending data on the side-channel socket...
131 while ((nfds
= poll(&pfd
, 1,
132 timeout
< 0.0 ? -1 : (int)(timeout
* 1000))) < 0 &&
133 (errno
== EINTR
|| errno
== EAGAIN
))
138 FD_SET(CUPS_SC_FD
, &input_set
);
140 stimeout
.tv_sec
= (int)timeout
;
141 stimeout
.tv_usec
= (int)(timeout
* 1000000) % 1000000;
143 while ((nfds
= select(CUPS_SC_FD
+ 1, &input_set
, NULL
, NULL
,
144 timeout
< 0.0 ? NULL
: &stimeout
)) < 0 &&
145 (errno
== EINTR
|| errno
== EAGAIN
))
148 #endif /* HAVE_POLL */
152 *command
= CUPS_SC_CMD_NONE
;
153 *status
= nfds
==0 ? CUPS_SC_STATUS_TIMEOUT
: CUPS_SC_STATUS_IO_ERROR
;
158 * Read a side-channel message for the format:
160 * Byte(s) Description
161 * ------- -------------------------------------------
164 * 2-3 Data length (network byte order)
168 if ((buffer
= _cupsBufferGet(_CUPS_SC_MAX_BUFFER
)) == NULL
)
170 *command
= CUPS_SC_CMD_NONE
;
171 *status
= CUPS_SC_STATUS_TOO_BIG
;
176 while ((bytes
= read(CUPS_SC_FD
, buffer
, _CUPS_SC_MAX_BUFFER
)) < 0)
177 if (errno
!= EINTR
&& errno
!= EAGAIN
)
179 DEBUG_printf(("1cupsSideChannelRead: Read error: %s", strerror(errno
)));
181 _cupsBufferRelease(buffer
);
183 *command
= CUPS_SC_CMD_NONE
;
184 *status
= CUPS_SC_STATUS_IO_ERROR
;
190 * Watch for EOF or too few bytes...
195 DEBUG_printf(("1cupsSideChannelRead: Short read of " CUPS_LLFMT
" bytes", CUPS_LLCAST bytes
));
197 _cupsBufferRelease(buffer
);
199 *command
= CUPS_SC_CMD_NONE
;
200 *status
= CUPS_SC_STATUS_BAD_MESSAGE
;
206 * Validate the command code in the message...
209 if (buffer
[0] < CUPS_SC_CMD_SOFT_RESET
||
210 buffer
[0] >= CUPS_SC_CMD_MAX
)
212 DEBUG_printf(("1cupsSideChannelRead: Bad command %d!", buffer
[0]));
214 _cupsBufferRelease(buffer
);
216 *command
= CUPS_SC_CMD_NONE
;
217 *status
= CUPS_SC_STATUS_BAD_MESSAGE
;
222 *command
= (cups_sc_command_t
)buffer
[0];
225 * Validate the data length in the message...
228 templen
= ((buffer
[2] & 255) << 8) | (buffer
[3] & 255);
230 if (templen
> 0 && (!data
|| !datalen
))
233 * Either the response is bigger than the provided buffer or the
234 * response is bigger than we've read...
237 *status
= CUPS_SC_STATUS_TOO_BIG
;
239 else if (!datalen
|| templen
> *datalen
|| templen
> (bytes
- 4))
242 * Either the response is bigger than the provided buffer or the
243 * response is bigger than we've read...
246 *status
= CUPS_SC_STATUS_TOO_BIG
;
251 * The response data will fit, copy it over and provide the actual
255 *status
= (cups_sc_status_t
)buffer
[1];
258 memcpy(data
, buffer
+ 4, (size_t)templen
);
261 _cupsBufferRelease(buffer
);
263 DEBUG_printf(("1cupsSideChannelRead: Returning status=%d", *status
));
270 * 'cupsSideChannelSNMPGet()' - Query a SNMP OID's value.
272 * This function asks the backend to do a SNMP OID query on behalf of the
273 * filter, port monitor, or backend using the default community name.
275 * "oid" contains a numeric OID consisting of integers separated by periods,
276 * for example ".1.3.6.1.2.1.43". Symbolic names from SNMP MIBs are not
277 * supported and must be converted to their numeric forms.
279 * On input, "data" and "datalen" provide the location and size of the
280 * buffer to hold the OID value as a string. HEX-String (binary) values are
281 * converted to hexadecimal strings representing the binary data, while
282 * NULL-Value and unknown OID types are returned as the empty string.
283 * The returned "datalen" does not include the trailing nul.
285 * @code CUPS_SC_STATUS_NOT_IMPLEMENTED@ is returned by backends that do not
286 * support SNMP queries. @code CUPS_SC_STATUS_NO_RESPONSE@ is returned when
287 * the printer does not respond to the SNMP query.
289 * @since CUPS 1.4/macOS 10.6@
292 cups_sc_status_t
/* O - Query status */
293 cupsSideChannelSNMPGet(
294 const char *oid
, /* I - OID to query */
295 char *data
, /* I - Buffer for OID value */
296 int *datalen
, /* IO - Size of OID buffer on entry, size of value on return */
297 double timeout
) /* I - Timeout in seconds */
299 cups_sc_status_t status
; /* Status of command */
300 cups_sc_command_t rcommand
; /* Response command */
301 char *real_data
; /* Real data buffer for response */
302 int real_datalen
, /* Real length of data buffer */
303 real_oidlen
; /* Length of returned OID string */
306 DEBUG_printf(("cupsSideChannelSNMPGet(oid=\"%s\", data=%p, datalen=%p(%d), "
307 "timeout=%.3f)", oid
, data
, datalen
, datalen
? *datalen
: -1,
311 * Range check input...
314 if (!oid
|| !*oid
|| !data
|| !datalen
|| *datalen
< 2)
315 return (CUPS_SC_STATUS_BAD_MESSAGE
);
320 * Send the request to the backend and wait for a response...
323 if (cupsSideChannelWrite(CUPS_SC_CMD_SNMP_GET
, CUPS_SC_STATUS_NONE
, oid
,
324 (int)strlen(oid
) + 1, timeout
))
325 return (CUPS_SC_STATUS_TIMEOUT
);
327 if ((real_data
= _cupsBufferGet(_CUPS_SC_MAX_BUFFER
)) == NULL
)
328 return (CUPS_SC_STATUS_TOO_BIG
);
330 real_datalen
= _CUPS_SC_MAX_BUFFER
;
331 if (cupsSideChannelRead(&rcommand
, &status
, real_data
, &real_datalen
, timeout
))
333 _cupsBufferRelease(real_data
);
334 return (CUPS_SC_STATUS_TIMEOUT
);
337 if (rcommand
!= CUPS_SC_CMD_SNMP_GET
)
339 _cupsBufferRelease(real_data
);
340 return (CUPS_SC_STATUS_BAD_MESSAGE
);
343 if (status
== CUPS_SC_STATUS_OK
)
346 * Parse the response of the form "oid\0value"...
349 real_oidlen
= (int)strlen(real_data
) + 1;
350 real_datalen
-= real_oidlen
;
352 if ((real_datalen
+ 1) > *datalen
)
354 _cupsBufferRelease(real_data
);
355 return (CUPS_SC_STATUS_TOO_BIG
);
358 memcpy(data
, real_data
+ real_oidlen
, (size_t)real_datalen
);
359 data
[real_datalen
] = '\0';
361 *datalen
= real_datalen
;
364 _cupsBufferRelease(real_data
);
371 * 'cupsSideChannelSNMPWalk()' - Query multiple SNMP OID values.
373 * This function asks the backend to do multiple SNMP OID queries on behalf
374 * of the filter, port monitor, or backend using the default community name.
375 * All OIDs under the "parent" OID are queried and the results are sent to
376 * the callback function you provide.
378 * "oid" contains a numeric OID consisting of integers separated by periods,
379 * for example ".1.3.6.1.2.1.43". Symbolic names from SNMP MIBs are not
380 * supported and must be converted to their numeric forms.
382 * "timeout" specifies the timeout for each OID query. The total amount of
383 * time will depend on the number of OID values found and the time required
386 * "cb" provides a function to call for every value that is found. "context"
387 * is an application-defined pointer that is sent to the callback function
388 * along with the OID and current data. The data passed to the callback is the
389 * same as returned by @link cupsSideChannelSNMPGet@.
391 * @code CUPS_SC_STATUS_NOT_IMPLEMENTED@ is returned by backends that do not
392 * support SNMP queries. @code CUPS_SC_STATUS_NO_RESPONSE@ is returned when
393 * the printer does not respond to the first SNMP query.
395 * @since CUPS 1.4/macOS 10.6@
398 cups_sc_status_t
/* O - Status of first query of @code CUPS_SC_STATUS_OK@ on success */
399 cupsSideChannelSNMPWalk(
400 const char *oid
, /* I - First numeric OID to query */
401 double timeout
, /* I - Timeout for each query in seconds */
402 cups_sc_walk_func_t cb
, /* I - Function to call with each value */
403 void *context
) /* I - Application-defined pointer to send to callback */
405 cups_sc_status_t status
; /* Status of command */
406 cups_sc_command_t rcommand
; /* Response command */
407 char *real_data
; /* Real data buffer for response */
408 int real_datalen
; /* Real length of data buffer */
409 size_t real_oidlen
, /* Length of returned OID string */
410 oidlen
; /* Length of first OID */
411 const char *current_oid
; /* Current OID */
412 char last_oid
[2048]; /* Last OID */
415 DEBUG_printf(("cupsSideChannelSNMPWalk(oid=\"%s\", timeout=%.3f, cb=%p, "
416 "context=%p)", oid
, timeout
, cb
, context
));
419 * Range check input...
422 if (!oid
|| !*oid
|| !cb
)
423 return (CUPS_SC_STATUS_BAD_MESSAGE
);
425 if ((real_data
= _cupsBufferGet(_CUPS_SC_MAX_BUFFER
)) == NULL
)
426 return (CUPS_SC_STATUS_TOO_BIG
);
429 * Loop until the OIDs don't match...
433 oidlen
= strlen(oid
);
439 * Send the request to the backend and wait for a response...
442 if (cupsSideChannelWrite(CUPS_SC_CMD_SNMP_GET_NEXT
, CUPS_SC_STATUS_NONE
,
443 current_oid
, (int)strlen(current_oid
) + 1, timeout
))
445 _cupsBufferRelease(real_data
);
446 return (CUPS_SC_STATUS_TIMEOUT
);
449 real_datalen
= _CUPS_SC_MAX_BUFFER
;
450 if (cupsSideChannelRead(&rcommand
, &status
, real_data
, &real_datalen
,
453 _cupsBufferRelease(real_data
);
454 return (CUPS_SC_STATUS_TIMEOUT
);
457 if (rcommand
!= CUPS_SC_CMD_SNMP_GET_NEXT
)
459 _cupsBufferRelease(real_data
);
460 return (CUPS_SC_STATUS_BAD_MESSAGE
);
463 if (status
== CUPS_SC_STATUS_OK
)
466 * Parse the response of the form "oid\0value"...
469 if (strncmp(real_data
, oid
, oidlen
) || real_data
[oidlen
] != '.' ||
470 !strcmp(real_data
, last_oid
))
473 * Done with this set of OIDs...
476 _cupsBufferRelease(real_data
);
477 return (CUPS_SC_STATUS_OK
);
480 if ((size_t)real_datalen
< sizeof(real_data
))
481 real_data
[real_datalen
] = '\0';
483 real_oidlen
= strlen(real_data
) + 1;
484 real_datalen
-= (int)real_oidlen
;
487 * Call the callback with the OID and data...
490 (*cb
)(real_data
, real_data
+ real_oidlen
, real_datalen
, context
);
493 * Update the current OID...
496 current_oid
= real_data
;
497 strlcpy(last_oid
, current_oid
, sizeof(last_oid
));
500 while (status
== CUPS_SC_STATUS_OK
);
502 _cupsBufferRelease(real_data
);
509 * 'cupsSideChannelWrite()' - Write a side-channel message.
511 * This function is normally only called by backend programs to send
512 * responses to a filter, driver, or port monitor program.
514 * @since CUPS 1.3/macOS 10.5@
517 int /* O - 0 on success, -1 on error */
518 cupsSideChannelWrite(
519 cups_sc_command_t command
, /* I - Command code */
520 cups_sc_status_t status
, /* I - Status code */
521 const char *data
, /* I - Data buffer pointer */
522 int datalen
, /* I - Number of bytes of data */
523 double timeout
) /* I - Timeout in seconds */
525 char *buffer
; /* Message buffer */
526 ssize_t bytes
; /* Bytes written */
528 struct pollfd pfd
; /* Poll structure for poll() */
530 fd_set output_set
; /* Output set for select() */
531 struct timeval stimeout
; /* Timeout value for select() */
532 #endif /* HAVE_POLL */
536 * Range check input...
539 if (command
< CUPS_SC_CMD_SOFT_RESET
|| command
>= CUPS_SC_CMD_MAX
||
540 datalen
< 0 || datalen
> _CUPS_SC_MAX_DATA
|| (datalen
> 0 && !data
))
544 * See if we can safely write to the side-channel socket...
549 pfd
.events
= POLLOUT
;
553 if (poll(&pfd
, 1, -1) < 1)
556 else if (poll(&pfd
, 1, (int)(timeout
* 1000)) < 1)
560 FD_ZERO(&output_set
);
561 FD_SET(CUPS_SC_FD
, &output_set
);
565 if (select(CUPS_SC_FD
+ 1, NULL
, &output_set
, NULL
, NULL
) < 1)
570 stimeout
.tv_sec
= (int)timeout
;
571 stimeout
.tv_usec
= (int)(timeout
* 1000000) % 1000000;
573 if (select(CUPS_SC_FD
+ 1, NULL
, &output_set
, NULL
, &stimeout
) < 1)
576 #endif /* HAVE_POLL */
579 * Write a side-channel message in the format:
581 * Byte(s) Description
582 * ------- -------------------------------------------
585 * 2-3 Data length (network byte order) <= 16384
589 if ((buffer
= _cupsBufferGet((size_t)datalen
+ 4)) == NULL
)
594 buffer
[2] = (char)(datalen
>> 8);
595 buffer
[3] = (char)(datalen
& 255);
601 memcpy(buffer
+ 4, data
, (size_t)datalen
);
605 while (write(CUPS_SC_FD
, buffer
, (size_t)bytes
) < 0)
606 if (errno
!= EINTR
&& errno
!= EAGAIN
)
608 _cupsBufferRelease(buffer
);
612 _cupsBufferRelease(buffer
);