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