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