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