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