]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/sidechannel.c
Update svn:keyword properties.
[thirdparty/cups.git] / cups / sidechannel.c
1 /*
2 * "$Id$"
3 *
4 * Side-channel API code for CUPS.
5 *
6 * Copyright 2007-2012 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 * Contents:
18 *
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.
25 */
26
27 /*
28 * Include necessary headers...
29 */
30
31 #include "sidechannel.h"
32 #include "cups-private.h"
33 #ifdef WIN32
34 # include <io.h>
35 #else
36 # include <unistd.h>
37 #endif /* WIN32 */
38 #ifdef __hpux
39 # include <sys/time.h>
40 #elif !defined(WIN32)
41 # include <sys/select.h>
42 #endif /* __hpux */
43 #ifndef WIN32
44 # include <sys/time.h>
45 #endif /* !WIN32 */
46 #ifdef HAVE_POLL
47 # include <poll.h>
48 #endif /* HAVE_POLL */
49
50
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
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 *
72 * @since CUPS 1.3/OS X 10.5@
73 */
74
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 */
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 *
111 * @since CUPS 1.3/OS X 10.5@
112 */
113
114 int /* O - 0 on success, -1 on error */
115 cupsSideChannelRead(
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 {
122 char *buffer; /* Message buffer */
123 int bytes; /* Bytes read */
124 int templen; /* Data length from message */
125 int nfds; /* Number of file descriptors */
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
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));
137
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
153 while ((nfds = poll(&pfd, 1,
154 timeout < 0.0 ? -1 : (long)(timeout * 1000))) < 0 &&
155 (errno == EINTR || errno == EAGAIN))
156 ;
157
158 #else /* select() */
159 FD_ZERO(&input_set);
160 FD_SET(CUPS_SC_FD, &input_set);
161
162 stimeout.tv_sec = (int)timeout;
163 stimeout.tv_usec = (int)(timeout * 1000000) % 1000000;
164
165 while ((nfds = select(CUPS_SC_FD + 1, &input_set, NULL, NULL,
166 timeout < 0.0 ? NULL : &stimeout)) < 0 &&
167 (errno == EINTR || errno == EAGAIN))
168 ;
169
170 #endif /* HAVE_POLL */
171
172 if (nfds < 1)
173 {
174 *command = CUPS_SC_CMD_NONE;
175 *status = nfds==0 ? CUPS_SC_STATUS_TIMEOUT : CUPS_SC_STATUS_IO_ERROR;
176 return (-1);
177 }
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
186 * 2-3 Data length (network byte order)
187 * 4-N Data
188 */
189
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)
199 if (errno != EINTR && errno != EAGAIN)
200 {
201 DEBUG_printf(("1cupsSideChannelRead: Read error: %s", strerror(errno)));
202
203 _cupsBufferRelease(buffer);
204
205 *command = CUPS_SC_CMD_NONE;
206 *status = CUPS_SC_STATUS_IO_ERROR;
207
208 return (-1);
209 }
210
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));
218
219 _cupsBufferRelease(buffer);
220
221 *command = CUPS_SC_CMD_NONE;
222 *status = CUPS_SC_STATUS_BAD_MESSAGE;
223
224 return (-1);
225 }
226
227 /*
228 * Validate the command code in the message...
229 */
230
231 if (buffer[0] < CUPS_SC_CMD_SOFT_RESET ||
232 buffer[0] >= CUPS_SC_CMD_MAX)
233 {
234 DEBUG_printf(("1cupsSideChannelRead: Bad command %d!", buffer[0]));
235
236 _cupsBufferRelease(buffer);
237
238 *command = CUPS_SC_CMD_NONE;
239 *status = CUPS_SC_STATUS_BAD_MESSAGE;
240
241 return (-1);
242 }
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 }
261 else if (!datalen || templen > *datalen || templen > (bytes - 4))
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
283 _cupsBufferRelease(buffer);
284
285 DEBUG_printf(("1cupsSideChannelRead: Returning status=%d", *status));
286
287 return (0);
288 }
289
290
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 *
311 * @since CUPS 1.4/OS X 10.6@
312 */
313
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 */
320 {
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 */
326
327
328 DEBUG_printf(("cupsSideChannelSNMPGet(oid=\"%s\", data=%p, datalen=%p(%d), "
329 "timeout=%.3f)", oid, data, datalen, datalen ? *datalen : -1,
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,
346 (int)strlen(oid) + 1, timeout))
347 return (CUPS_SC_STATUS_TIMEOUT);
348
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;
353 if (cupsSideChannelRead(&rcommand, &status, real_data, &real_datalen, timeout))
354 {
355 _cupsBufferRelease(real_data);
356 return (CUPS_SC_STATUS_TIMEOUT);
357 }
358
359 if (rcommand != CUPS_SC_CMD_SNMP_GET)
360 {
361 _cupsBufferRelease(real_data);
362 return (CUPS_SC_STATUS_BAD_MESSAGE);
363 }
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)
375 {
376 _cupsBufferRelease(real_data);
377 return (CUPS_SC_STATUS_TOO_BIG);
378 }
379
380 memcpy(data, real_data + real_oidlen, real_datalen);
381 data[real_datalen] = '\0';
382
383 *datalen = real_datalen;
384 }
385
386 _cupsBufferRelease(real_data);
387
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 *
417 * @since CUPS 1.4/OS X 10.6@
418 */
419
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 */
426 {
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 */
435
436
437 DEBUG_printf(("cupsSideChannelSNMPWalk(oid=\"%s\", timeout=%.3f, cb=%p, "
438 "context=%p)", oid, timeout, cb, context));
439
440 /*
441 * Range check input...
442 */
443
444 if (!oid || !*oid || !cb)
445 return (CUPS_SC_STATUS_BAD_MESSAGE);
446
447 if ((real_data = _cupsBufferGet(_CUPS_SC_MAX_BUFFER)) == NULL)
448 return (CUPS_SC_STATUS_TOO_BIG);
449
450 /*
451 * Loop until the OIDs don't match...
452 */
453
454 current_oid = oid;
455 oidlen = (int)strlen(oid);
456 last_oid[0] = '\0';
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,
465 current_oid, (int)strlen(current_oid) + 1, timeout))
466 {
467 _cupsBufferRelease(real_data);
468 return (CUPS_SC_STATUS_TIMEOUT);
469 }
470
471 real_datalen = _CUPS_SC_MAX_BUFFER;
472 if (cupsSideChannelRead(&rcommand, &status, real_data, &real_datalen,
473 timeout))
474 {
475 _cupsBufferRelease(real_data);
476 return (CUPS_SC_STATUS_TIMEOUT);
477 }
478
479 if (rcommand != CUPS_SC_CMD_SNMP_GET_NEXT)
480 {
481 _cupsBufferRelease(real_data);
482 return (CUPS_SC_STATUS_BAD_MESSAGE);
483 }
484
485 if (status == CUPS_SC_STATUS_OK)
486 {
487 /*
488 * Parse the response of the form "oid\0value"...
489 */
490
491 if (strncmp(real_data, oid, oidlen) || real_data[oidlen] != '.' ||
492 !strcmp(real_data, last_oid))
493 {
494 /*
495 * Done with this set of OIDs...
496 */
497
498 _cupsBufferRelease(real_data);
499 return (CUPS_SC_STATUS_OK);
500 }
501
502 if (real_datalen < sizeof(real_data))
503 real_data[real_datalen] = '\0';
504
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
512 (*cb)(real_data, real_data + real_oidlen, real_datalen, context);
513
514 /*
515 * Update the current OID...
516 */
517
518 current_oid = real_data;
519 strlcpy(last_oid, current_oid, sizeof(last_oid));
520 }
521 }
522 while (status == CUPS_SC_STATUS_OK);
523
524 _cupsBufferRelease(real_data);
525
526 return (status);
527 }
528
529
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 *
536 * @since CUPS 1.3/OS X 10.5@
537 */
538
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 */
546 {
547 char *buffer; /* Message buffer */
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
561 if (command < CUPS_SC_CMD_SOFT_RESET || command >= CUPS_SC_CMD_MAX ||
562 datalen < 0 || datalen > _CUPS_SC_MAX_DATA || (datalen > 0 && !data))
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
611 if ((buffer = _cupsBufferGet(datalen + 4)) == NULL)
612 return (-1);
613
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)
629 {
630 _cupsBufferRelease(buffer);
631 return (-1);
632 }
633
634 _cupsBufferRelease(buffer);
635
636 return (0);
637 }
638
639
640 /*
641 * End of "$Id$".
642 */