]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/sidechannel.c
Drop old private APIs that are no longer used/supported.
[thirdparty/cups.git] / cups / sidechannel.c
CommitLineData
f7deaa1a 1/*
f2d18633 2 * "$Id$"
f7deaa1a 3 *
71e16022 4 * Side-channel API code for CUPS.
f7deaa1a 5 *
82cc1f9a 6 * Copyright 2007-2012 by Apple Inc.
f7deaa1a 7 * Copyright 2006 by Easy Software Products.
8 *
9 * These coded instructions, statements, and computer programs are the
bc44d920 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/".
f7deaa1a 14 *
15 * This file is subject to the Apple OS-Developed Software exception.
16 *
17 * Contents:
18 *
20fbc903
MS
19 * cupsSideChannelDoRequest() - Send a side-channel command to a backend and
20 * wait for a response.
f7deaa1a 21 * cupsSideChannelRead() - Read a side-channel message.
20fbc903
MS
22 * cupsSideChannelSNMPGet() - Query a SNMP OID's value.
23 * cupsSideChannelSNMPWalk() - Query multiple SNMP OID values.
f7deaa1a 24 * cupsSideChannelWrite() - Write a side-channel message.
25 */
26
27/*
28 * Include necessary headers...
29 */
30
31#include "sidechannel.h"
dcb445bc 32#include "cups-private.h"
536bc2c6
MS
33#ifdef WIN32
34# include <io.h>
35#else
36# include <unistd.h>
37#endif /* WIN32 */
f7deaa1a 38#ifdef __hpux
39# include <sys/time.h>
536bc2c6 40#elif !defined(WIN32)
f7deaa1a 41# include <sys/select.h>
42#endif /* __hpux */
43#ifndef WIN32
44# include <sys/time.h>
45#endif /* !WIN32 */
46#ifdef HAVE_POLL
dcb445bc 47# include <poll.h>
f7deaa1a 48#endif /* HAVE_POLL */
49
50
dcb445bc
MS
51/*
52 * Buffer size for side-channel requests...
53 */
54
55#define _CUPS_SC_MAX_DATA 65535
56#define _CUPS_SC_MAX_BUFFER 65540
57
58
f7deaa1a 59/*
60 * 'cupsSideChannelDoRequest()' - Send a side-channel command to a backend and wait for a response.
61 *
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.
67 *
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.
71 *
f3c17241 72 * @since CUPS 1.3/OS X 10.5@
f7deaa1a 73 */
74
75cups_sc_status_t /* O - Status of command */
76cupsSideChannelDoRequest(
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 */
81{
82 cups_sc_status_t status; /* Status of command */
83 cups_sc_command_t rcommand; /* Response command */
84
85
86 if (cupsSideChannelWrite(command, CUPS_SC_STATUS_NONE, NULL, 0, timeout))
87 return (CUPS_SC_STATUS_TIMEOUT);
88
89 if (cupsSideChannelRead(&rcommand, &status, data, datalen, timeout))
90 return (CUPS_SC_STATUS_TIMEOUT);
91
92 if (rcommand != command)
93 return (CUPS_SC_STATUS_BAD_MESSAGE);
94
95 return (status);
96}
97
98
99/*
100 * 'cupsSideChannelRead()' - Read a side-channel message.
101 *
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.
106 *
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.
110 *
f3c17241 111 * @since CUPS 1.3/OS X 10.5@
f7deaa1a 112 */
113
114int /* O - 0 on success, -1 on error */
115cupsSideChannelRead(
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 */
121{
dcb445bc 122 char *buffer; /* Message buffer */
f7deaa1a 123 int bytes; /* Bytes read */
124 int templen; /* Data length from message */
ef55b745 125 int nfds; /* Number of file descriptors */
f7deaa1a 126#ifdef HAVE_POLL
127 struct pollfd pfd; /* Poll structure for poll() */
128#else /* select() */
129 fd_set input_set; /* Input set for select() */
130 struct timeval stimeout; /* Timeout value for select() */
131#endif /* HAVE_POLL */
132
133
20fbc903 134 DEBUG_printf(("cupsSideChannelRead(command=%p, status=%p, data=%p, "
e07d4801 135 "datalen=%p(%d), timeout=%.3f)", command, status, data,
20fbc903
MS
136 datalen, datalen ? *datalen : -1, timeout));
137
f7deaa1a 138 /*
139 * Range check input...
140 */
141
142 if (!command || !status)
143 return (-1);
144
145 /*
146 * See if we have pending data on the side-channel socket...
147 */
148
149#ifdef HAVE_POLL
150 pfd.fd = CUPS_SC_FD;
151 pfd.events = POLLIN;
152
82cc1f9a 153 while ((nfds = poll(&pfd, 1,
ef55b745
MS
154 timeout < 0.0 ? -1 : (long)(timeout * 1000))) < 0 &&
155 (errno == EINTR || errno == EAGAIN))
156 ;
f7deaa1a 157
158#else /* select() */
159 FD_ZERO(&input_set);
160 FD_SET(CUPS_SC_FD, &input_set);
161
ef55b745
MS
162 stimeout.tv_sec = (int)timeout;
163 stimeout.tv_usec = (int)(timeout * 1000000) % 1000000;
164
82cc1f9a 165 while ((nfds = select(CUPS_SC_FD + 1, &input_set, NULL, NULL,
ef55b745
MS
166 timeout < 0.0 ? NULL : &stimeout)) < 0 &&
167 (errno == EINTR || errno == EAGAIN))
168 ;
169
170#endif /* HAVE_POLL */
171
82cc1f9a 172 if (nfds < 1)
f7deaa1a 173 {
dcb445bc
MS
174 *command = CUPS_SC_CMD_NONE;
175 *status = nfds==0 ? CUPS_SC_STATUS_TIMEOUT : CUPS_SC_STATUS_IO_ERROR;
ef55b745 176 return (-1);
f7deaa1a 177 }
f7deaa1a 178
179 /*
180 * Read a side-channel message for the format:
181 *
182 * Byte(s) Description
183 * ------- -------------------------------------------
184 * 0 Command code
185 * 1 Status code
dcb445bc 186 * 2-3 Data length (network byte order)
f7deaa1a 187 * 4-N Data
188 */
189
dcb445bc
MS
190 if ((buffer = _cupsBufferGet(_CUPS_SC_MAX_BUFFER)) == NULL)
191 {
192 *command = CUPS_SC_CMD_NONE;
193 *status = CUPS_SC_STATUS_TOO_BIG;
194
195 return (-1);
196 }
197
198 while ((bytes = read(CUPS_SC_FD, buffer, _CUPS_SC_MAX_BUFFER)) < 0)
f7deaa1a 199 if (errno != EINTR && errno != EAGAIN)
20fbc903 200 {
e07d4801 201 DEBUG_printf(("1cupsSideChannelRead: Read error: %s", strerror(errno)));
dcb445bc
MS
202
203 _cupsBufferRelease(buffer);
204
18ecb428
MS
205 *command = CUPS_SC_CMD_NONE;
206 *status = CUPS_SC_STATUS_IO_ERROR;
dcb445bc 207
f7deaa1a 208 return (-1);
20fbc903 209 }
f7deaa1a 210
18ecb428
MS
211 /*
212 * Watch for EOF or too few bytes...
213 */
214
215 if (bytes < 4)
216 {
217 DEBUG_printf(("1cupsSideChannelRead: Short read of %d bytes", bytes));
dcb445bc
MS
218
219 _cupsBufferRelease(buffer);
220
18ecb428
MS
221 *command = CUPS_SC_CMD_NONE;
222 *status = CUPS_SC_STATUS_BAD_MESSAGE;
dcb445bc 223
18ecb428
MS
224 return (-1);
225 }
226
f7deaa1a 227 /*
228 * Validate the command code in the message...
229 */
230
20fbc903 231 if (buffer[0] < CUPS_SC_CMD_SOFT_RESET ||
f14324a7 232 buffer[0] >= CUPS_SC_CMD_MAX)
20fbc903 233 {
e07d4801 234 DEBUG_printf(("1cupsSideChannelRead: Bad command %d!", buffer[0]));
dcb445bc
MS
235
236 _cupsBufferRelease(buffer);
237
18ecb428
MS
238 *command = CUPS_SC_CMD_NONE;
239 *status = CUPS_SC_STATUS_BAD_MESSAGE;
dcb445bc 240
f7deaa1a 241 return (-1);
20fbc903 242 }
f7deaa1a 243
244 *command = (cups_sc_command_t)buffer[0];
245
246 /*
247 * Validate the data length in the message...
248 */
249
250 templen = ((buffer[2] & 255) << 8) | (buffer[3] & 255);
251
252 if (templen > 0 && (!data || !datalen))
253 {
254 /*
255 * Either the response is bigger than the provided buffer or the
256 * response is bigger than we've read...
257 */
258
259 *status = CUPS_SC_STATUS_TOO_BIG;
260 }
1f0275e3 261 else if (!datalen || templen > *datalen || templen > (bytes - 4))
f7deaa1a 262 {
263 /*
264 * Either the response is bigger than the provided buffer or the
265 * response is bigger than we've read...
266 */
267
268 *status = CUPS_SC_STATUS_TOO_BIG;
269 }
270 else
271 {
272 /*
273 * The response data will fit, copy it over and provide the actual
274 * length...
275 */
276
277 *status = (cups_sc_status_t)buffer[1];
278 *datalen = templen;
279
280 memcpy(data, buffer + 4, templen);
281 }
282
dcb445bc
MS
283 _cupsBufferRelease(buffer);
284
e07d4801 285 DEBUG_printf(("1cupsSideChannelRead: Returning status=%d", *status));
20fbc903 286
f7deaa1a 287 return (0);
288}
289
290
20fbc903
MS
291/*
292 * 'cupsSideChannelSNMPGet()' - Query a SNMP OID's value.
293 *
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.
296 *
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.
300 *
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.
306 *
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.
310 *
f3c17241 311 * @since CUPS 1.4/OS X 10.6@
20fbc903
MS
312 */
313
314cups_sc_status_t /* O - Query status */
315cupsSideChannelSNMPGet(
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 */
320{
321 cups_sc_status_t status; /* Status of command */
322 cups_sc_command_t rcommand; /* Response command */
dcb445bc 323 char *real_data; /* Real data buffer for response */
20fbc903
MS
324 int real_datalen, /* Real length of data buffer */
325 real_oidlen; /* Length of returned OID string */
326
327
328 DEBUG_printf(("cupsSideChannelSNMPGet(oid=\"%s\", data=%p, datalen=%p(%d), "
e07d4801 329 "timeout=%.3f)", oid, data, datalen, datalen ? *datalen : -1,
20fbc903
MS
330 timeout));
331
332 /*
333 * Range check input...
334 */
335
336 if (!oid || !*oid || !data || !datalen || *datalen < 2)
337 return (CUPS_SC_STATUS_BAD_MESSAGE);
338
339 *data = '\0';
340
341 /*
342 * Send the request to the backend and wait for a response...
343 */
344
345 if (cupsSideChannelWrite(CUPS_SC_CMD_SNMP_GET, CUPS_SC_STATUS_NONE, oid,
b9faaae1 346 (int)strlen(oid) + 1, timeout))
20fbc903
MS
347 return (CUPS_SC_STATUS_TIMEOUT);
348
dcb445bc
MS
349 if ((real_data = _cupsBufferGet(_CUPS_SC_MAX_BUFFER)) == NULL)
350 return (CUPS_SC_STATUS_TOO_BIG);
351
352 real_datalen = _CUPS_SC_MAX_BUFFER;
20fbc903 353 if (cupsSideChannelRead(&rcommand, &status, real_data, &real_datalen, timeout))
dcb445bc
MS
354 {
355 _cupsBufferRelease(real_data);
20fbc903 356 return (CUPS_SC_STATUS_TIMEOUT);
dcb445bc 357 }
20fbc903
MS
358
359 if (rcommand != CUPS_SC_CMD_SNMP_GET)
dcb445bc
MS
360 {
361 _cupsBufferRelease(real_data);
20fbc903 362 return (CUPS_SC_STATUS_BAD_MESSAGE);
dcb445bc 363 }
20fbc903
MS
364
365 if (status == CUPS_SC_STATUS_OK)
366 {
367 /*
368 * Parse the response of the form "oid\0value"...
369 */
370
371 real_oidlen = strlen(real_data) + 1;
372 real_datalen -= real_oidlen;
373
374 if ((real_datalen + 1) > *datalen)
dcb445bc
MS
375 {
376 _cupsBufferRelease(real_data);
20fbc903 377 return (CUPS_SC_STATUS_TOO_BIG);
dcb445bc 378 }
20fbc903
MS
379
380 memcpy(data, real_data + real_oidlen, real_datalen);
381 data[real_datalen] = '\0';
382
383 *datalen = real_datalen;
384 }
385
dcb445bc
MS
386 _cupsBufferRelease(real_data);
387
20fbc903
MS
388 return (status);
389}
390
391
392/*
393 * 'cupsSideChannelSNMPWalk()' - Query multiple SNMP OID values.
394 *
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.
399 *
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.
403 *
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
406 * for each query.
407 *
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@.
412 *
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.
416 *
f3c17241 417 * @since CUPS 1.4/OS X 10.6@
20fbc903
MS
418 */
419
420cups_sc_status_t /* O - Status of first query of @code CUPS_SC_STATUS_OK@ on success */
421cupsSideChannelSNMPWalk(
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 */
426{
427 cups_sc_status_t status; /* Status of command */
428 cups_sc_command_t rcommand; /* Response command */
dcb445bc 429 char *real_data; /* Real data buffer for response */
20fbc903
MS
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 */
18ecb428 434 char last_oid[2048]; /* Last OID */
20fbc903
MS
435
436
437 DEBUG_printf(("cupsSideChannelSNMPWalk(oid=\"%s\", timeout=%.3f, cb=%p, "
e07d4801 438 "context=%p)", oid, timeout, cb, context));
20fbc903
MS
439
440 /*
441 * Range check input...
442 */
443
444 if (!oid || !*oid || !cb)
445 return (CUPS_SC_STATUS_BAD_MESSAGE);
446
dcb445bc
MS
447 if ((real_data = _cupsBufferGet(_CUPS_SC_MAX_BUFFER)) == NULL)
448 return (CUPS_SC_STATUS_TOO_BIG);
449
20fbc903
MS
450 /*
451 * Loop until the OIDs don't match...
452 */
453
454 current_oid = oid;
455 oidlen = (int)strlen(oid);
18ecb428 456 last_oid[0] = '\0';
20fbc903
MS
457
458 do
459 {
460 /*
461 * Send the request to the backend and wait for a response...
462 */
463
464 if (cupsSideChannelWrite(CUPS_SC_CMD_SNMP_GET_NEXT, CUPS_SC_STATUS_NONE,
b9faaae1 465 current_oid, (int)strlen(current_oid) + 1, timeout))
dcb445bc
MS
466 {
467 _cupsBufferRelease(real_data);
20fbc903 468 return (CUPS_SC_STATUS_TIMEOUT);
dcb445bc 469 }
20fbc903 470
dcb445bc 471 real_datalen = _CUPS_SC_MAX_BUFFER;
20fbc903
MS
472 if (cupsSideChannelRead(&rcommand, &status, real_data, &real_datalen,
473 timeout))
dcb445bc
MS
474 {
475 _cupsBufferRelease(real_data);
20fbc903 476 return (CUPS_SC_STATUS_TIMEOUT);
dcb445bc 477 }
20fbc903
MS
478
479 if (rcommand != CUPS_SC_CMD_SNMP_GET_NEXT)
dcb445bc
MS
480 {
481 _cupsBufferRelease(real_data);
20fbc903 482 return (CUPS_SC_STATUS_BAD_MESSAGE);
dcb445bc 483 }
20fbc903
MS
484
485 if (status == CUPS_SC_STATUS_OK)
486 {
487 /*
488 * Parse the response of the form "oid\0value"...
489 */
490
18ecb428
MS
491 if (strncmp(real_data, oid, oidlen) || real_data[oidlen] != '.' ||
492 !strcmp(real_data, last_oid))
20fbc903
MS
493 {
494 /*
495 * Done with this set of OIDs...
496 */
497
dcb445bc 498 _cupsBufferRelease(real_data);
20fbc903
MS
499 return (CUPS_SC_STATUS_OK);
500 }
501
d1c13e16
MS
502 if (real_datalen < sizeof(real_data))
503 real_data[real_datalen] = '\0';
504
20fbc903
MS
505 real_oidlen = strlen(real_data) + 1;
506 real_datalen -= real_oidlen;
507
508 /*
509 * Call the callback with the OID and data...
510 */
511
82cc1f9a 512 (*cb)(real_data, real_data + real_oidlen, real_datalen, context);
20fbc903
MS
513
514 /*
515 * Update the current OID...
516 */
517
518 current_oid = real_data;
18ecb428 519 strlcpy(last_oid, current_oid, sizeof(last_oid));
20fbc903
MS
520 }
521 }
522 while (status == CUPS_SC_STATUS_OK);
523
dcb445bc
MS
524 _cupsBufferRelease(real_data);
525
20fbc903
MS
526 return (status);
527}
528
529
f7deaa1a 530/*
531 * 'cupsSideChannelWrite()' - Write a side-channel message.
532 *
533 * This function is normally only called by backend programs to send
534 * responses to a filter, driver, or port monitor program.
535 *
f3c17241 536 * @since CUPS 1.3/OS X 10.5@
f7deaa1a 537 */
538
539int /* O - 0 on success, -1 on error */
540cupsSideChannelWrite(
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 */
546{
dcb445bc 547 char *buffer; /* Message buffer */
f7deaa1a 548 int bytes; /* Bytes written */
549#ifdef HAVE_POLL
550 struct pollfd pfd; /* Poll structure for poll() */
551#else /* select() */
552 fd_set output_set; /* Output set for select() */
553 struct timeval stimeout; /* Timeout value for select() */
554#endif /* HAVE_POLL */
555
556
557 /*
558 * Range check input...
559 */
560
f14324a7 561 if (command < CUPS_SC_CMD_SOFT_RESET || command >= CUPS_SC_CMD_MAX ||
dcb445bc 562 datalen < 0 || datalen > _CUPS_SC_MAX_DATA || (datalen > 0 && !data))
f7deaa1a 563 return (-1);
564
565 /*
566 * See if we can safely write to the side-channel socket...
567 */
568
569#ifdef HAVE_POLL
570 pfd.fd = CUPS_SC_FD;
571 pfd.events = POLLOUT;
572
573 if (timeout < 0.0)
574 {
575 if (poll(&pfd, 1, -1) < 1)
576 return (-1);
577 }
578 else if (poll(&pfd, 1, (long)(timeout * 1000)) < 1)
579 return (-1);
580
581#else /* select() */
582 FD_ZERO(&output_set);
583 FD_SET(CUPS_SC_FD, &output_set);
584
585 if (timeout < 0.0)
586 {
587 if (select(CUPS_SC_FD + 1, NULL, &output_set, NULL, NULL) < 1)
588 return (-1);
589 }
590 else
591 {
592 stimeout.tv_sec = (int)timeout;
593 stimeout.tv_usec = (int)(timeout * 1000000) % 1000000;
594
595 if (select(CUPS_SC_FD + 1, NULL, &output_set, NULL, &stimeout) < 1)
596 return (-1);
597 }
598#endif /* HAVE_POLL */
599
600 /*
601 * Write a side-channel message in the format:
602 *
603 * Byte(s) Description
604 * ------- -------------------------------------------
605 * 0 Command code
606 * 1 Status code
607 * 2-3 Data length (network byte order) <= 16384
608 * 4-N Data
609 */
610
dcb445bc
MS
611 if ((buffer = _cupsBufferGet(datalen + 4)) == NULL)
612 return (-1);
613
f7deaa1a 614 buffer[0] = command;
615 buffer[1] = status;
616 buffer[2] = datalen >> 8;
617 buffer[3] = datalen & 255;
618
619 bytes = 4;
620
621 if (datalen > 0)
622 {
623 memcpy(buffer + 4, data, datalen);
624 bytes += datalen;
625 }
626
627 while (write(CUPS_SC_FD, buffer, bytes) < 0)
628 if (errno != EINTR && errno != EAGAIN)
dcb445bc
MS
629 {
630 _cupsBufferRelease(buffer);
f7deaa1a 631 return (-1);
dcb445bc
MS
632 }
633
634 _cupsBufferRelease(buffer);
f7deaa1a 635
636 return (0);
637}
638
639
640/*
f2d18633 641 * End of "$Id$".
f7deaa1a 642 */