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