/src/cups/cups/sidechannel.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Side-channel API code for CUPS. |
3 | | * |
4 | | * Copyright 2007-2014 by Apple Inc. |
5 | | * Copyright 2006 by Easy Software Products. |
6 | | * |
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 |
11 | | * missing or damaged, see the license at "http://www.cups.org/". |
12 | | * |
13 | | * This file is subject to the Apple OS-Developed Software exception. |
14 | | */ |
15 | | |
16 | | /* |
17 | | * Include necessary headers... |
18 | | */ |
19 | | |
20 | | #include "sidechannel.h" |
21 | | #include "cups-private.h" |
22 | | #ifdef _WIN32 |
23 | | # include <io.h> |
24 | | #else |
25 | | # include <unistd.h> |
26 | | #endif /* _WIN32 */ |
27 | | #ifndef _WIN32 |
28 | | # include <sys/select.h> |
29 | | # include <sys/time.h> |
30 | | #endif /* !_WIN32 */ |
31 | | #ifdef HAVE_POLL |
32 | | # include <poll.h> |
33 | | #endif /* HAVE_POLL */ |
34 | | |
35 | | |
36 | | /* |
37 | | * Buffer size for side-channel requests... |
38 | | */ |
39 | | |
40 | 0 | #define _CUPS_SC_MAX_DATA 65535 |
41 | 0 | #define _CUPS_SC_MAX_BUFFER 65540 |
42 | | |
43 | | |
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 | | * |
57 | | * @since CUPS 1.3/macOS 10.5@ |
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 | 0 | { |
67 | 0 | cups_sc_status_t status; /* Status of command */ |
68 | 0 | cups_sc_command_t rcommand; /* Response command */ |
69 | | |
70 | |
|
71 | 0 | if (cupsSideChannelWrite(command, CUPS_SC_STATUS_NONE, NULL, 0, timeout)) |
72 | 0 | return (CUPS_SC_STATUS_TIMEOUT); |
73 | | |
74 | 0 | if (cupsSideChannelRead(&rcommand, &status, data, datalen, timeout)) |
75 | 0 | return (CUPS_SC_STATUS_TIMEOUT); |
76 | | |
77 | 0 | if (rcommand != command) |
78 | 0 | return (CUPS_SC_STATUS_BAD_MESSAGE); |
79 | | |
80 | 0 | return (status); |
81 | 0 | } |
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 | | * |
96 | | * @since CUPS 1.3/macOS 10.5@ |
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 | 0 | { |
107 | 0 | char *buffer; /* Message buffer */ |
108 | 0 | ssize_t bytes; /* Bytes read */ |
109 | 0 | int templen; /* Data length from message */ |
110 | 0 | int nfds; /* Number of file descriptors */ |
111 | 0 | #ifdef HAVE_POLL |
112 | 0 | 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 | |
|
119 | 0 | DEBUG_printf(("cupsSideChannelRead(command=%p, status=%p, data=%p, " |
120 | 0 | "datalen=%p(%d), timeout=%.3f)", command, status, data, |
121 | 0 | datalen, datalen ? *datalen : -1, timeout)); |
122 | | |
123 | | /* |
124 | | * Range check input... |
125 | | */ |
126 | |
|
127 | 0 | if (!command || !status) |
128 | 0 | return (-1); |
129 | | |
130 | | /* |
131 | | * See if we have pending data on the side-channel socket... |
132 | | */ |
133 | | |
134 | 0 | #ifdef HAVE_POLL |
135 | 0 | pfd.fd = CUPS_SC_FD; |
136 | 0 | pfd.events = POLLIN; |
137 | |
|
138 | 0 | while ((nfds = poll(&pfd, 1, |
139 | 0 | timeout < 0.0 ? -1 : (int)(timeout * 1000))) < 0 && |
140 | 0 | (errno == EINTR || errno == EAGAIN)) |
141 | 0 | ; |
142 | |
|
143 | | #else /* select() */ |
144 | | FD_ZERO(&input_set); |
145 | | FD_SET(CUPS_SC_FD, &input_set); |
146 | | |
147 | | stimeout.tv_sec = (int)timeout; |
148 | | stimeout.tv_usec = (int)(timeout * 1000000) % 1000000; |
149 | | |
150 | | while ((nfds = select(CUPS_SC_FD + 1, &input_set, NULL, NULL, |
151 | | timeout < 0.0 ? NULL : &stimeout)) < 0 && |
152 | | (errno == EINTR || errno == EAGAIN)) |
153 | | ; |
154 | | |
155 | | #endif /* HAVE_POLL */ |
156 | |
|
157 | 0 | if (nfds < 1) |
158 | 0 | { |
159 | 0 | *command = CUPS_SC_CMD_NONE; |
160 | 0 | *status = nfds==0 ? CUPS_SC_STATUS_TIMEOUT : CUPS_SC_STATUS_IO_ERROR; |
161 | 0 | return (-1); |
162 | 0 | } |
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 |
171 | | * 2-3 Data length (network byte order) |
172 | | * 4-N Data |
173 | | */ |
174 | | |
175 | 0 | if ((buffer = _cupsBufferGet(_CUPS_SC_MAX_BUFFER)) == NULL) |
176 | 0 | { |
177 | 0 | *command = CUPS_SC_CMD_NONE; |
178 | 0 | *status = CUPS_SC_STATUS_TOO_BIG; |
179 | |
|
180 | 0 | return (-1); |
181 | 0 | } |
182 | | |
183 | 0 | while ((bytes = read(CUPS_SC_FD, buffer, _CUPS_SC_MAX_BUFFER)) < 0) |
184 | 0 | if (errno != EINTR && errno != EAGAIN) |
185 | 0 | { |
186 | 0 | DEBUG_printf(("1cupsSideChannelRead: Read error: %s", strerror(errno))); |
187 | |
|
188 | 0 | _cupsBufferRelease(buffer); |
189 | |
|
190 | 0 | *command = CUPS_SC_CMD_NONE; |
191 | 0 | *status = CUPS_SC_STATUS_IO_ERROR; |
192 | |
|
193 | 0 | return (-1); |
194 | 0 | } |
195 | | |
196 | | /* |
197 | | * Watch for EOF or too few bytes... |
198 | | */ |
199 | | |
200 | 0 | if (bytes < 4) |
201 | 0 | { |
202 | 0 | DEBUG_printf(("1cupsSideChannelRead: Short read of " CUPS_LLFMT " bytes", CUPS_LLCAST bytes)); |
203 | |
|
204 | 0 | _cupsBufferRelease(buffer); |
205 | |
|
206 | 0 | *command = CUPS_SC_CMD_NONE; |
207 | 0 | *status = CUPS_SC_STATUS_BAD_MESSAGE; |
208 | |
|
209 | 0 | return (-1); |
210 | 0 | } |
211 | | |
212 | | /* |
213 | | * Validate the command code in the message... |
214 | | */ |
215 | | |
216 | 0 | if (buffer[0] < CUPS_SC_CMD_SOFT_RESET || |
217 | 0 | buffer[0] >= CUPS_SC_CMD_MAX) |
218 | 0 | { |
219 | 0 | DEBUG_printf(("1cupsSideChannelRead: Bad command %d!", buffer[0])); |
220 | |
|
221 | 0 | _cupsBufferRelease(buffer); |
222 | |
|
223 | 0 | *command = CUPS_SC_CMD_NONE; |
224 | 0 | *status = CUPS_SC_STATUS_BAD_MESSAGE; |
225 | |
|
226 | 0 | return (-1); |
227 | 0 | } |
228 | | |
229 | 0 | *command = (cups_sc_command_t)buffer[0]; |
230 | | |
231 | | /* |
232 | | * Validate the data length in the message... |
233 | | */ |
234 | |
|
235 | 0 | templen = ((buffer[2] & 255) << 8) | (buffer[3] & 255); |
236 | |
|
237 | 0 | if (templen > 0 && (!data || !datalen)) |
238 | 0 | { |
239 | | /* |
240 | | * Either the response is bigger than the provided buffer or the |
241 | | * response is bigger than we've read... |
242 | | */ |
243 | |
|
244 | 0 | *status = CUPS_SC_STATUS_TOO_BIG; |
245 | 0 | } |
246 | 0 | else if (!datalen || templen > *datalen || templen > (bytes - 4)) |
247 | 0 | { |
248 | | /* |
249 | | * Either the response is bigger than the provided buffer or the |
250 | | * response is bigger than we've read... |
251 | | */ |
252 | |
|
253 | 0 | *status = CUPS_SC_STATUS_TOO_BIG; |
254 | 0 | } |
255 | 0 | else |
256 | 0 | { |
257 | | /* |
258 | | * The response data will fit, copy it over and provide the actual |
259 | | * length... |
260 | | */ |
261 | |
|
262 | 0 | *status = (cups_sc_status_t)buffer[1]; |
263 | 0 | *datalen = templen; |
264 | |
|
265 | 0 | memcpy(data, buffer + 4, (size_t)templen); |
266 | 0 | } |
267 | |
|
268 | 0 | _cupsBufferRelease(buffer); |
269 | |
|
270 | 0 | DEBUG_printf(("1cupsSideChannelRead: Returning status=%d", *status)); |
271 | |
|
272 | 0 | return (0); |
273 | 0 | } |
274 | | |
275 | | |
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 | | * |
296 | | * @since CUPS 1.4/macOS 10.6@ |
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 | 0 | { |
306 | 0 | cups_sc_status_t status; /* Status of command */ |
307 | 0 | cups_sc_command_t rcommand; /* Response command */ |
308 | 0 | char *real_data; /* Real data buffer for response */ |
309 | 0 | int real_datalen, /* Real length of data buffer */ |
310 | 0 | real_oidlen; /* Length of returned OID string */ |
311 | | |
312 | |
|
313 | 0 | DEBUG_printf(("cupsSideChannelSNMPGet(oid=\"%s\", data=%p, datalen=%p(%d), " |
314 | 0 | "timeout=%.3f)", oid, data, datalen, datalen ? *datalen : -1, |
315 | 0 | timeout)); |
316 | | |
317 | | /* |
318 | | * Range check input... |
319 | | */ |
320 | |
|
321 | 0 | if (!oid || !*oid || !data || !datalen || *datalen < 2) |
322 | 0 | return (CUPS_SC_STATUS_BAD_MESSAGE); |
323 | | |
324 | 0 | *data = '\0'; |
325 | | |
326 | | /* |
327 | | * Send the request to the backend and wait for a response... |
328 | | */ |
329 | |
|
330 | 0 | if (cupsSideChannelWrite(CUPS_SC_CMD_SNMP_GET, CUPS_SC_STATUS_NONE, oid, |
331 | 0 | (int)strlen(oid) + 1, timeout)) |
332 | 0 | return (CUPS_SC_STATUS_TIMEOUT); |
333 | | |
334 | 0 | if ((real_data = _cupsBufferGet(_CUPS_SC_MAX_BUFFER)) == NULL) |
335 | 0 | return (CUPS_SC_STATUS_TOO_BIG); |
336 | | |
337 | 0 | real_datalen = _CUPS_SC_MAX_BUFFER; |
338 | 0 | if (cupsSideChannelRead(&rcommand, &status, real_data, &real_datalen, timeout)) |
339 | 0 | { |
340 | 0 | _cupsBufferRelease(real_data); |
341 | 0 | return (CUPS_SC_STATUS_TIMEOUT); |
342 | 0 | } |
343 | | |
344 | 0 | if (rcommand != CUPS_SC_CMD_SNMP_GET) |
345 | 0 | { |
346 | 0 | _cupsBufferRelease(real_data); |
347 | 0 | return (CUPS_SC_STATUS_BAD_MESSAGE); |
348 | 0 | } |
349 | | |
350 | 0 | if (status == CUPS_SC_STATUS_OK) |
351 | 0 | { |
352 | | /* |
353 | | * Parse the response of the form "oid\0value"... |
354 | | */ |
355 | |
|
356 | 0 | real_oidlen = (int)strlen(real_data) + 1; |
357 | 0 | real_datalen -= real_oidlen; |
358 | |
|
359 | 0 | if ((real_datalen + 1) > *datalen) |
360 | 0 | { |
361 | 0 | _cupsBufferRelease(real_data); |
362 | 0 | return (CUPS_SC_STATUS_TOO_BIG); |
363 | 0 | } |
364 | | |
365 | 0 | memcpy(data, real_data + real_oidlen, (size_t)real_datalen); |
366 | 0 | data[real_datalen] = '\0'; |
367 | |
|
368 | 0 | *datalen = real_datalen; |
369 | 0 | } |
370 | | |
371 | 0 | _cupsBufferRelease(real_data); |
372 | |
|
373 | 0 | return (status); |
374 | 0 | } |
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 | | * |
402 | | * @since CUPS 1.4/macOS 10.6@ |
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 | 0 | { |
412 | 0 | cups_sc_status_t status; /* Status of command */ |
413 | 0 | cups_sc_command_t rcommand; /* Response command */ |
414 | 0 | char *real_data; /* Real data buffer for response */ |
415 | 0 | int real_datalen; /* Real length of data buffer */ |
416 | 0 | size_t real_oidlen, /* Length of returned OID string */ |
417 | 0 | oidlen; /* Length of first OID */ |
418 | 0 | const char *current_oid; /* Current OID */ |
419 | 0 | char last_oid[2048]; /* Last OID */ |
420 | | |
421 | |
|
422 | 0 | DEBUG_printf(("cupsSideChannelSNMPWalk(oid=\"%s\", timeout=%.3f, cb=%p, " |
423 | 0 | "context=%p)", oid, timeout, cb, context)); |
424 | | |
425 | | /* |
426 | | * Range check input... |
427 | | */ |
428 | |
|
429 | 0 | if (!oid || !*oid || !cb) |
430 | 0 | return (CUPS_SC_STATUS_BAD_MESSAGE); |
431 | | |
432 | 0 | if ((real_data = _cupsBufferGet(_CUPS_SC_MAX_BUFFER)) == NULL) |
433 | 0 | return (CUPS_SC_STATUS_TOO_BIG); |
434 | | |
435 | | /* |
436 | | * Loop until the OIDs don't match... |
437 | | */ |
438 | | |
439 | 0 | current_oid = oid; |
440 | 0 | oidlen = strlen(oid); |
441 | 0 | last_oid[0] = '\0'; |
442 | |
|
443 | 0 | do |
444 | 0 | { |
445 | | /* |
446 | | * Send the request to the backend and wait for a response... |
447 | | */ |
448 | |
|
449 | 0 | if (cupsSideChannelWrite(CUPS_SC_CMD_SNMP_GET_NEXT, CUPS_SC_STATUS_NONE, |
450 | 0 | current_oid, (int)strlen(current_oid) + 1, timeout)) |
451 | 0 | { |
452 | 0 | _cupsBufferRelease(real_data); |
453 | 0 | return (CUPS_SC_STATUS_TIMEOUT); |
454 | 0 | } |
455 | | |
456 | 0 | real_datalen = _CUPS_SC_MAX_BUFFER; |
457 | 0 | if (cupsSideChannelRead(&rcommand, &status, real_data, &real_datalen, |
458 | 0 | timeout)) |
459 | 0 | { |
460 | 0 | _cupsBufferRelease(real_data); |
461 | 0 | return (CUPS_SC_STATUS_TIMEOUT); |
462 | 0 | } |
463 | | |
464 | 0 | if (rcommand != CUPS_SC_CMD_SNMP_GET_NEXT) |
465 | 0 | { |
466 | 0 | _cupsBufferRelease(real_data); |
467 | 0 | return (CUPS_SC_STATUS_BAD_MESSAGE); |
468 | 0 | } |
469 | | |
470 | 0 | if (status == CUPS_SC_STATUS_OK) |
471 | 0 | { |
472 | | /* |
473 | | * Parse the response of the form "oid\0value"... |
474 | | */ |
475 | |
|
476 | 0 | if (strncmp(real_data, oid, oidlen) || real_data[oidlen] != '.' || |
477 | 0 | !strcmp(real_data, last_oid)) |
478 | 0 | { |
479 | | /* |
480 | | * Done with this set of OIDs... |
481 | | */ |
482 | |
|
483 | 0 | _cupsBufferRelease(real_data); |
484 | 0 | return (CUPS_SC_STATUS_OK); |
485 | 0 | } |
486 | | |
487 | 0 | if ((size_t)real_datalen < sizeof(real_data)) |
488 | 0 | real_data[real_datalen] = '\0'; |
489 | |
|
490 | 0 | real_oidlen = strlen(real_data) + 1; |
491 | 0 | real_datalen -= (int)real_oidlen; |
492 | | |
493 | | /* |
494 | | * Call the callback with the OID and data... |
495 | | */ |
496 | |
|
497 | 0 | (*cb)(real_data, real_data + real_oidlen, real_datalen, context); |
498 | | |
499 | | /* |
500 | | * Update the current OID... |
501 | | */ |
502 | |
|
503 | 0 | current_oid = real_data; |
504 | 0 | strlcpy(last_oid, current_oid, sizeof(last_oid)); |
505 | 0 | } |
506 | 0 | } |
507 | 0 | while (status == CUPS_SC_STATUS_OK); |
508 | | |
509 | 0 | _cupsBufferRelease(real_data); |
510 | |
|
511 | 0 | return (status); |
512 | 0 | } |
513 | | |
514 | | |
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 | | * |
521 | | * @since CUPS 1.3/macOS 10.5@ |
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 | 0 | { |
532 | 0 | char *buffer; /* Message buffer */ |
533 | 0 | ssize_t bytes; /* Bytes written */ |
534 | 0 | #ifdef HAVE_POLL |
535 | 0 | 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 | |
|
546 | 0 | if (command < CUPS_SC_CMD_SOFT_RESET || command >= CUPS_SC_CMD_MAX || |
547 | 0 | datalen < 0 || datalen > _CUPS_SC_MAX_DATA || (datalen > 0 && !data)) |
548 | 0 | return (-1); |
549 | | |
550 | | /* |
551 | | * See if we can safely write to the side-channel socket... |
552 | | */ |
553 | | |
554 | 0 | #ifdef HAVE_POLL |
555 | 0 | pfd.fd = CUPS_SC_FD; |
556 | 0 | pfd.events = POLLOUT; |
557 | |
|
558 | 0 | if (timeout < 0.0) |
559 | 0 | { |
560 | 0 | if (poll(&pfd, 1, -1) < 1) |
561 | 0 | return (-1); |
562 | 0 | } |
563 | 0 | else if (poll(&pfd, 1, (int)(timeout * 1000)) < 1) |
564 | 0 | 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 | | |
596 | 0 | if ((buffer = _cupsBufferGet((size_t)datalen + 4)) == NULL) |
597 | 0 | return (-1); |
598 | | |
599 | 0 | buffer[0] = command; |
600 | 0 | buffer[1] = status; |
601 | 0 | buffer[2] = (char)(datalen >> 8); |
602 | 0 | buffer[3] = (char)(datalen & 255); |
603 | |
|
604 | 0 | bytes = 4; |
605 | |
|
606 | 0 | if (datalen > 0) |
607 | 0 | { |
608 | 0 | memcpy(buffer + 4, data, (size_t)datalen); |
609 | 0 | bytes += datalen; |
610 | 0 | } |
611 | |
|
612 | 0 | while (write(CUPS_SC_FD, buffer, (size_t)bytes) < 0) |
613 | 0 | if (errno != EINTR && errno != EAGAIN) |
614 | 0 | { |
615 | 0 | _cupsBufferRelease(buffer); |
616 | 0 | return (-1); |
617 | 0 | } |
618 | | |
619 | 0 | _cupsBufferRelease(buffer); |
620 | |
|
621 | 0 | return (0); |
622 | 0 | } |