]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/sidechannel.c
d7638372d9fd19b928aed7b694d04e1c1e40e56f
[thirdparty/cups.git] / cups / sidechannel.c
1 /*
2 * "$Id$"
3 *
4 * Side-channel API code for CUPS.
5 *
6 * Copyright 2007-2014 by Apple Inc.
7 * Copyright 2006 by Easy Software Products.
8 *
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/".
14 *
15 * This file is subject to the Apple OS-Developed Software exception.
16 */
17
18 /*
19 * Include necessary headers...
20 */
21
22 #include "sidechannel.h"
23 #include "cups-private.h"
24 #ifdef WIN32
25 # include <io.h>
26 #else
27 # include <unistd.h>
28 #endif /* WIN32 */
29 #ifndef WIN32
30 # include <sys/select.h>
31 # include <sys/time.h>
32 #endif /* !WIN32 */
33 #ifdef HAVE_POLL
34 # include <poll.h>
35 #endif /* HAVE_POLL */
36
37
38 /*
39 * Buffer size for side-channel requests...
40 */
41
42 #define _CUPS_SC_MAX_DATA 65535
43 #define _CUPS_SC_MAX_BUFFER 65540
44
45
46 /*
47 * 'cupsSideChannelDoRequest()' - Send a side-channel command to a backend and wait for a response.
48 *
49 * This function is normally only called by filters, drivers, or port
50 * monitors in order to communicate with the backend used by the current
51 * printer. Programs must be prepared to handle timeout or "not
52 * implemented" status codes, which indicate that the backend or device
53 * do not support the specified side-channel command.
54 *
55 * The "datalen" parameter must be initialized to the size of the buffer
56 * pointed to by the "data" parameter. cupsSideChannelDoRequest() will
57 * update the value to contain the number of data bytes in the buffer.
58 *
59 * @since CUPS 1.3/OS X 10.5@
60 */
61
62 cups_sc_status_t /* O - Status of command */
63 cupsSideChannelDoRequest(
64 cups_sc_command_t command, /* I - Command to send */
65 char *data, /* O - Response data buffer pointer */
66 int *datalen, /* IO - Size of data buffer on entry, number of bytes in buffer on return */
67 double timeout) /* I - Timeout in seconds */
68 {
69 cups_sc_status_t status; /* Status of command */
70 cups_sc_command_t rcommand; /* Response command */
71
72
73 if (cupsSideChannelWrite(command, CUPS_SC_STATUS_NONE, NULL, 0, timeout))
74 return (CUPS_SC_STATUS_TIMEOUT);
75
76 if (cupsSideChannelRead(&rcommand, &status, data, datalen, timeout))
77 return (CUPS_SC_STATUS_TIMEOUT);
78
79 if (rcommand != command)
80 return (CUPS_SC_STATUS_BAD_MESSAGE);
81
82 return (status);
83 }
84
85
86 /*
87 * 'cupsSideChannelRead()' - Read a side-channel message.
88 *
89 * This function is normally only called by backend programs to read
90 * commands from a filter, driver, or port monitor program. The
91 * caller must be prepared to handle incomplete or invalid messages
92 * and return the corresponding status codes.
93 *
94 * The "datalen" parameter must be initialized to the size of the buffer
95 * pointed to by the "data" parameter. cupsSideChannelDoRequest() will
96 * update the value to contain the number of data bytes in the buffer.
97 *
98 * @since CUPS 1.3/OS X 10.5@
99 */
100
101 int /* O - 0 on success, -1 on error */
102 cupsSideChannelRead(
103 cups_sc_command_t *command, /* O - Command code */
104 cups_sc_status_t *status, /* O - Status code */
105 char *data, /* O - Data buffer pointer */
106 int *datalen, /* IO - Size of data buffer on entry, number of bytes in buffer on return */
107 double timeout) /* I - Timeout in seconds */
108 {
109 char *buffer; /* Message buffer */
110 ssize_t bytes; /* Bytes read */
111 int templen; /* Data length from message */
112 int nfds; /* Number of file descriptors */
113 #ifdef HAVE_POLL
114 struct pollfd pfd; /* Poll structure for poll() */
115 #else /* select() */
116 fd_set input_set; /* Input set for select() */
117 struct timeval stimeout; /* Timeout value for select() */
118 #endif /* HAVE_POLL */
119
120
121 DEBUG_printf(("cupsSideChannelRead(command=%p, status=%p, data=%p, "
122 "datalen=%p(%d), timeout=%.3f)", command, status, data,
123 datalen, datalen ? *datalen : -1, timeout));
124
125 /*
126 * Range check input...
127 */
128
129 if (!command || !status)
130 return (-1);
131
132 /*
133 * See if we have pending data on the side-channel socket...
134 */
135
136 #ifdef HAVE_POLL
137 pfd.fd = CUPS_SC_FD;
138 pfd.events = POLLIN;
139
140 while ((nfds = poll(&pfd, 1,
141 timeout < 0.0 ? -1 : (int)(timeout * 1000))) < 0 &&
142 (errno == EINTR || errno == EAGAIN))
143 ;
144
145 #else /* select() */
146 FD_ZERO(&input_set);
147 FD_SET(CUPS_SC_FD, &input_set);
148
149 stimeout.tv_sec = (int)timeout;
150 stimeout.tv_usec = (int)(timeout * 1000000) % 1000000;
151
152 while ((nfds = select(CUPS_SC_FD + 1, &input_set, NULL, NULL,
153 timeout < 0.0 ? NULL : &stimeout)) < 0 &&
154 (errno == EINTR || errno == EAGAIN))
155 ;
156
157 #endif /* HAVE_POLL */
158
159 if (nfds < 1)
160 {
161 *command = CUPS_SC_CMD_NONE;
162 *status = nfds==0 ? CUPS_SC_STATUS_TIMEOUT : CUPS_SC_STATUS_IO_ERROR;
163 return (-1);
164 }
165
166 /*
167 * Read a side-channel message for the format:
168 *
169 * Byte(s) Description
170 * ------- -------------------------------------------
171 * 0 Command code
172 * 1 Status code
173 * 2-3 Data length (network byte order)
174 * 4-N Data
175 */
176
177 if ((buffer = _cupsBufferGet(_CUPS_SC_MAX_BUFFER)) == NULL)
178 {
179 *command = CUPS_SC_CMD_NONE;
180 *status = CUPS_SC_STATUS_TOO_BIG;
181
182 return (-1);
183 }
184
185 while ((bytes = read(CUPS_SC_FD, buffer, _CUPS_SC_MAX_BUFFER)) < 0)
186 if (errno != EINTR && errno != EAGAIN)
187 {
188 DEBUG_printf(("1cupsSideChannelRead: Read error: %s", strerror(errno)));
189
190 _cupsBufferRelease(buffer);
191
192 *command = CUPS_SC_CMD_NONE;
193 *status = CUPS_SC_STATUS_IO_ERROR;
194
195 return (-1);
196 }
197
198 /*
199 * Watch for EOF or too few bytes...
200 */
201
202 if (bytes < 4)
203 {
204 DEBUG_printf(("1cupsSideChannelRead: Short read of " CUPS_LLFMT " bytes", CUPS_LLCAST bytes));
205
206 _cupsBufferRelease(buffer);
207
208 *command = CUPS_SC_CMD_NONE;
209 *status = CUPS_SC_STATUS_BAD_MESSAGE;
210
211 return (-1);
212 }
213
214 /*
215 * Validate the command code in the message...
216 */
217
218 if (buffer[0] < CUPS_SC_CMD_SOFT_RESET ||
219 buffer[0] >= CUPS_SC_CMD_MAX)
220 {
221 DEBUG_printf(("1cupsSideChannelRead: Bad command %d!", buffer[0]));
222
223 _cupsBufferRelease(buffer);
224
225 *command = CUPS_SC_CMD_NONE;
226 *status = CUPS_SC_STATUS_BAD_MESSAGE;
227
228 return (-1);
229 }
230
231 *command = (cups_sc_command_t)buffer[0];
232
233 /*
234 * Validate the data length in the message...
235 */
236
237 templen = ((buffer[2] & 255) << 8) | (buffer[3] & 255);
238
239 if (templen > 0 && (!data || !datalen))
240 {
241 /*
242 * Either the response is bigger than the provided buffer or the
243 * response is bigger than we've read...
244 */
245
246 *status = CUPS_SC_STATUS_TOO_BIG;
247 }
248 else if (!datalen || templen > *datalen || templen > (bytes - 4))
249 {
250 /*
251 * Either the response is bigger than the provided buffer or the
252 * response is bigger than we've read...
253 */
254
255 *status = CUPS_SC_STATUS_TOO_BIG;
256 }
257 else
258 {
259 /*
260 * The response data will fit, copy it over and provide the actual
261 * length...
262 */
263
264 *status = (cups_sc_status_t)buffer[1];
265 *datalen = templen;
266
267 memcpy(data, buffer + 4, templen);
268 }
269
270 _cupsBufferRelease(buffer);
271
272 DEBUG_printf(("1cupsSideChannelRead: Returning status=%d", *status));
273
274 return (0);
275 }
276
277
278 /*
279 * 'cupsSideChannelSNMPGet()' - Query a SNMP OID's value.
280 *
281 * This function asks the backend to do a SNMP OID query on behalf of the
282 * filter, port monitor, or backend using the default community name.
283 *
284 * "oid" contains a numeric OID consisting of integers separated by periods,
285 * for example ".1.3.6.1.2.1.43". Symbolic names from SNMP MIBs are not
286 * supported and must be converted to their numeric forms.
287 *
288 * On input, "data" and "datalen" provide the location and size of the
289 * buffer to hold the OID value as a string. HEX-String (binary) values are
290 * converted to hexadecimal strings representing the binary data, while
291 * NULL-Value and unknown OID types are returned as the empty string.
292 * The returned "datalen" does not include the trailing nul.
293 *
294 * @code CUPS_SC_STATUS_NOT_IMPLEMENTED@ is returned by backends that do not
295 * support SNMP queries. @code CUPS_SC_STATUS_NO_RESPONSE@ is returned when
296 * the printer does not respond to the SNMP query.
297 *
298 * @since CUPS 1.4/OS X 10.6@
299 */
300
301 cups_sc_status_t /* O - Query status */
302 cupsSideChannelSNMPGet(
303 const char *oid, /* I - OID to query */
304 char *data, /* I - Buffer for OID value */
305 int *datalen, /* IO - Size of OID buffer on entry, size of value on return */
306 double timeout) /* I - Timeout in seconds */
307 {
308 cups_sc_status_t status; /* Status of command */
309 cups_sc_command_t rcommand; /* Response command */
310 char *real_data; /* Real data buffer for response */
311 int real_datalen, /* Real length of data buffer */
312 real_oidlen; /* Length of returned OID string */
313
314
315 DEBUG_printf(("cupsSideChannelSNMPGet(oid=\"%s\", data=%p, datalen=%p(%d), "
316 "timeout=%.3f)", oid, data, datalen, datalen ? *datalen : -1,
317 timeout));
318
319 /*
320 * Range check input...
321 */
322
323 if (!oid || !*oid || !data || !datalen || *datalen < 2)
324 return (CUPS_SC_STATUS_BAD_MESSAGE);
325
326 *data = '\0';
327
328 /*
329 * Send the request to the backend and wait for a response...
330 */
331
332 if (cupsSideChannelWrite(CUPS_SC_CMD_SNMP_GET, CUPS_SC_STATUS_NONE, oid,
333 (int)strlen(oid) + 1, timeout))
334 return (CUPS_SC_STATUS_TIMEOUT);
335
336 if ((real_data = _cupsBufferGet(_CUPS_SC_MAX_BUFFER)) == NULL)
337 return (CUPS_SC_STATUS_TOO_BIG);
338
339 real_datalen = _CUPS_SC_MAX_BUFFER;
340 if (cupsSideChannelRead(&rcommand, &status, real_data, &real_datalen, timeout))
341 {
342 _cupsBufferRelease(real_data);
343 return (CUPS_SC_STATUS_TIMEOUT);
344 }
345
346 if (rcommand != CUPS_SC_CMD_SNMP_GET)
347 {
348 _cupsBufferRelease(real_data);
349 return (CUPS_SC_STATUS_BAD_MESSAGE);
350 }
351
352 if (status == CUPS_SC_STATUS_OK)
353 {
354 /*
355 * Parse the response of the form "oid\0value"...
356 */
357
358 real_oidlen = (int)strlen(real_data) + 1;
359 real_datalen -= real_oidlen;
360
361 if ((real_datalen + 1) > *datalen)
362 {
363 _cupsBufferRelease(real_data);
364 return (CUPS_SC_STATUS_TOO_BIG);
365 }
366
367 memcpy(data, real_data + real_oidlen, real_datalen);
368 data[real_datalen] = '\0';
369
370 *datalen = real_datalen;
371 }
372
373 _cupsBufferRelease(real_data);
374
375 return (status);
376 }
377
378
379 /*
380 * 'cupsSideChannelSNMPWalk()' - Query multiple SNMP OID values.
381 *
382 * This function asks the backend to do multiple SNMP OID queries on behalf
383 * of the filter, port monitor, or backend using the default community name.
384 * All OIDs under the "parent" OID are queried and the results are sent to
385 * the callback function you provide.
386 *
387 * "oid" contains a numeric OID consisting of integers separated by periods,
388 * for example ".1.3.6.1.2.1.43". Symbolic names from SNMP MIBs are not
389 * supported and must be converted to their numeric forms.
390 *
391 * "timeout" specifies the timeout for each OID query. The total amount of
392 * time will depend on the number of OID values found and the time required
393 * for each query.
394 *
395 * "cb" provides a function to call for every value that is found. "context"
396 * is an application-defined pointer that is sent to the callback function
397 * along with the OID and current data. The data passed to the callback is the
398 * same as returned by @link cupsSideChannelSNMPGet@.
399 *
400 * @code CUPS_SC_STATUS_NOT_IMPLEMENTED@ is returned by backends that do not
401 * support SNMP queries. @code CUPS_SC_STATUS_NO_RESPONSE@ is returned when
402 * the printer does not respond to the first SNMP query.
403 *
404 * @since CUPS 1.4/OS X 10.6@
405 */
406
407 cups_sc_status_t /* O - Status of first query of @code CUPS_SC_STATUS_OK@ on success */
408 cupsSideChannelSNMPWalk(
409 const char *oid, /* I - First numeric OID to query */
410 double timeout, /* I - Timeout for each query in seconds */
411 cups_sc_walk_func_t cb, /* I - Function to call with each value */
412 void *context) /* I - Application-defined pointer to send to callback */
413 {
414 cups_sc_status_t status; /* Status of command */
415 cups_sc_command_t rcommand; /* Response command */
416 char *real_data; /* Real data buffer for response */
417 int real_datalen; /* Real length of data buffer */
418 size_t real_oidlen, /* Length of returned OID string */
419 oidlen; /* Length of first OID */
420 const char *current_oid; /* Current OID */
421 char last_oid[2048]; /* Last OID */
422
423
424 DEBUG_printf(("cupsSideChannelSNMPWalk(oid=\"%s\", timeout=%.3f, cb=%p, "
425 "context=%p)", oid, timeout, cb, context));
426
427 /*
428 * Range check input...
429 */
430
431 if (!oid || !*oid || !cb)
432 return (CUPS_SC_STATUS_BAD_MESSAGE);
433
434 if ((real_data = _cupsBufferGet(_CUPS_SC_MAX_BUFFER)) == NULL)
435 return (CUPS_SC_STATUS_TOO_BIG);
436
437 /*
438 * Loop until the OIDs don't match...
439 */
440
441 current_oid = oid;
442 oidlen = strlen(oid);
443 last_oid[0] = '\0';
444
445 do
446 {
447 /*
448 * Send the request to the backend and wait for a response...
449 */
450
451 if (cupsSideChannelWrite(CUPS_SC_CMD_SNMP_GET_NEXT, CUPS_SC_STATUS_NONE,
452 current_oid, (int)strlen(current_oid) + 1, timeout))
453 {
454 _cupsBufferRelease(real_data);
455 return (CUPS_SC_STATUS_TIMEOUT);
456 }
457
458 real_datalen = _CUPS_SC_MAX_BUFFER;
459 if (cupsSideChannelRead(&rcommand, &status, real_data, &real_datalen,
460 timeout))
461 {
462 _cupsBufferRelease(real_data);
463 return (CUPS_SC_STATUS_TIMEOUT);
464 }
465
466 if (rcommand != CUPS_SC_CMD_SNMP_GET_NEXT)
467 {
468 _cupsBufferRelease(real_data);
469 return (CUPS_SC_STATUS_BAD_MESSAGE);
470 }
471
472 if (status == CUPS_SC_STATUS_OK)
473 {
474 /*
475 * Parse the response of the form "oid\0value"...
476 */
477
478 if (strncmp(real_data, oid, oidlen) || real_data[oidlen] != '.' ||
479 !strcmp(real_data, last_oid))
480 {
481 /*
482 * Done with this set of OIDs...
483 */
484
485 _cupsBufferRelease(real_data);
486 return (CUPS_SC_STATUS_OK);
487 }
488
489 if ((size_t)real_datalen < sizeof(real_data))
490 real_data[real_datalen] = '\0';
491
492 real_oidlen = strlen(real_data) + 1;
493 real_datalen -= real_oidlen;
494
495 /*
496 * Call the callback with the OID and data...
497 */
498
499 (*cb)(real_data, real_data + real_oidlen, real_datalen, context);
500
501 /*
502 * Update the current OID...
503 */
504
505 current_oid = real_data;
506 strlcpy(last_oid, current_oid, sizeof(last_oid));
507 }
508 }
509 while (status == CUPS_SC_STATUS_OK);
510
511 _cupsBufferRelease(real_data);
512
513 return (status);
514 }
515
516
517 /*
518 * 'cupsSideChannelWrite()' - Write a side-channel message.
519 *
520 * This function is normally only called by backend programs to send
521 * responses to a filter, driver, or port monitor program.
522 *
523 * @since CUPS 1.3/OS X 10.5@
524 */
525
526 int /* O - 0 on success, -1 on error */
527 cupsSideChannelWrite(
528 cups_sc_command_t command, /* I - Command code */
529 cups_sc_status_t status, /* I - Status code */
530 const char *data, /* I - Data buffer pointer */
531 int datalen, /* I - Number of bytes of data */
532 double timeout) /* I - Timeout in seconds */
533 {
534 char *buffer; /* Message buffer */
535 ssize_t bytes; /* Bytes written */
536 #ifdef HAVE_POLL
537 struct pollfd pfd; /* Poll structure for poll() */
538 #else /* select() */
539 fd_set output_set; /* Output set for select() */
540 struct timeval stimeout; /* Timeout value for select() */
541 #endif /* HAVE_POLL */
542
543
544 /*
545 * Range check input...
546 */
547
548 if (command < CUPS_SC_CMD_SOFT_RESET || command >= CUPS_SC_CMD_MAX ||
549 datalen < 0 || datalen > _CUPS_SC_MAX_DATA || (datalen > 0 && !data))
550 return (-1);
551
552 /*
553 * See if we can safely write to the side-channel socket...
554 */
555
556 #ifdef HAVE_POLL
557 pfd.fd = CUPS_SC_FD;
558 pfd.events = POLLOUT;
559
560 if (timeout < 0.0)
561 {
562 if (poll(&pfd, 1, -1) < 1)
563 return (-1);
564 }
565 else if (poll(&pfd, 1, (int)(timeout * 1000)) < 1)
566 return (-1);
567
568 #else /* select() */
569 FD_ZERO(&output_set);
570 FD_SET(CUPS_SC_FD, &output_set);
571
572 if (timeout < 0.0)
573 {
574 if (select(CUPS_SC_FD + 1, NULL, &output_set, NULL, NULL) < 1)
575 return (-1);
576 }
577 else
578 {
579 stimeout.tv_sec = (int)timeout;
580 stimeout.tv_usec = (int)(timeout * 1000000) % 1000000;
581
582 if (select(CUPS_SC_FD + 1, NULL, &output_set, NULL, &stimeout) < 1)
583 return (-1);
584 }
585 #endif /* HAVE_POLL */
586
587 /*
588 * Write a side-channel message in the format:
589 *
590 * Byte(s) Description
591 * ------- -------------------------------------------
592 * 0 Command code
593 * 1 Status code
594 * 2-3 Data length (network byte order) <= 16384
595 * 4-N Data
596 */
597
598 if ((buffer = _cupsBufferGet((size_t)datalen + 4)) == NULL)
599 return (-1);
600
601 buffer[0] = command;
602 buffer[1] = status;
603 buffer[2] = (char)(datalen >> 8);
604 buffer[3] = (char)(datalen & 255);
605
606 bytes = 4;
607
608 if (datalen > 0)
609 {
610 memcpy(buffer + 4, data, datalen);
611 bytes += datalen;
612 }
613
614 while (write(CUPS_SC_FD, buffer, (size_t)bytes) < 0)
615 if (errno != EINTR && errno != EAGAIN)
616 {
617 _cupsBufferRelease(buffer);
618 return (-1);
619 }
620
621 _cupsBufferRelease(buffer);
622
623 return (0);
624 }
625
626
627 /*
628 * End of "$Id$".
629 */