]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/sidechannel.c
Load cups into easysw/current.
[thirdparty/cups.git] / cups / sidechannel.c
1 /*
2 * "$Id: sidechannel.c 6649 2007-07-11 21:46:42Z mike $"
3 *
4 * Side-channel API code for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 2007 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
20 * and wait for a response.
21 * cupsSideChannelRead() - Read a side-channel message.
22 * cupsSideChannelWrite() - Write a side-channel message.
23 */
24
25 /*
26 * Include necessary headers...
27 */
28
29 #include "sidechannel.h"
30 #include "string.h"
31 #include <unistd.h>
32 #include <errno.h>
33 #ifdef __hpux
34 # include <sys/time.h>
35 #else
36 # include <sys/select.h>
37 #endif /* __hpux */
38 #ifndef WIN32
39 # include <sys/time.h>
40 #endif /* !WIN32 */
41 #ifdef HAVE_POLL
42 # include <sys/poll.h>
43 #endif /* HAVE_POLL */
44
45
46 /*
47 * 'cupsSideChannelDoRequest()' - Send a side-channel command to a backend and wait for a response.
48 *
49 * This function is normally only called by filters, drivers, or port
50 * monitors in order to communicate with the backend used by the current
51 * printer. Programs must be prepared to handle timeout or "not
52 * implemented" status codes, which indicate that the backend or device
53 * do not support the specified side-channel command.
54 *
55 * The "datalen" parameter must be initialized to the size of the buffer
56 * pointed to by the "data" parameter. cupsSideChannelDoRequest() will
57 * update the value to contain the number of data bytes in the buffer.
58 *
59 * @since CUPS 1.3@
60 */
61
62 cups_sc_status_t /* O - Status of command */
63 cupsSideChannelDoRequest(
64 cups_sc_command_t command, /* I - Command to send */
65 char *data, /* O - Response data buffer pointer */
66 int *datalen, /* IO - Size of data buffer on entry, number of bytes in buffer on return */
67 double timeout) /* I - Timeout in seconds */
68 {
69 cups_sc_status_t status; /* Status of command */
70 cups_sc_command_t rcommand; /* Response command */
71
72
73 if (cupsSideChannelWrite(command, CUPS_SC_STATUS_NONE, NULL, 0, timeout))
74 return (CUPS_SC_STATUS_TIMEOUT);
75
76 if (cupsSideChannelRead(&rcommand, &status, data, datalen, timeout))
77 return (CUPS_SC_STATUS_TIMEOUT);
78
79 if (rcommand != command)
80 return (CUPS_SC_STATUS_BAD_MESSAGE);
81
82 return (status);
83 }
84
85
86 /*
87 * 'cupsSideChannelRead()' - Read a side-channel message.
88 *
89 * This function is normally only called by backend programs to read
90 * commands from a filter, driver, or port monitor program. The
91 * caller must be prepared to handle incomplete or invalid messages
92 * and return the corresponding status codes.
93 *
94 * The "datalen" parameter must be initialized to the size of the buffer
95 * pointed to by the "data" parameter. cupsSideChannelDoRequest() will
96 * update the value to contain the number of data bytes in the buffer.
97 *
98 * @since CUPS 1.3@
99 */
100
101 int /* O - 0 on success, -1 on error */
102 cupsSideChannelRead(
103 cups_sc_command_t *command, /* O - Command code */
104 cups_sc_status_t *status, /* O - Status code */
105 char *data, /* O - Data buffer pointer */
106 int *datalen, /* IO - Size of data buffer on entry, number of bytes in buffer on return */
107 double timeout) /* I - Timeout in seconds */
108 {
109 char buffer[16388]; /* Message buffer */
110 int bytes; /* Bytes read */
111 int templen; /* Data length from message */
112 #ifdef HAVE_POLL
113 struct pollfd pfd; /* Poll structure for poll() */
114 #else /* select() */
115 fd_set input_set; /* Input set for select() */
116 struct timeval stimeout; /* Timeout value for select() */
117 #endif /* HAVE_POLL */
118
119
120 /*
121 * Range check input...
122 */
123
124 if (!command || !status)
125 return (-1);
126
127 /*
128 * See if we have pending data on the side-channel socket...
129 */
130
131 #ifdef HAVE_POLL
132 pfd.fd = CUPS_SC_FD;
133 pfd.events = POLLIN;
134
135 if (timeout < 0.0)
136 {
137 if (poll(&pfd, 1, -1) < 1)
138 return (-1);
139 }
140 else if (poll(&pfd, 1, (long)(timeout * 1000)) < 1)
141 return (-1);
142
143 #else /* select() */
144 FD_ZERO(&input_set);
145 FD_SET(CUPS_SC_FD, &input_set);
146
147 if (timeout < 0.0)
148 {
149 if (select(CUPS_SC_FD + 1, &input_set, NULL, NULL, NULL) < 1)
150 return (-1);
151 }
152 else
153 {
154 stimeout.tv_sec = (int)timeout;
155 stimeout.tv_usec = (int)(timeout * 1000000) % 1000000;
156
157 if (select(CUPS_SC_FD + 1, &input_set, NULL, NULL, &stimeout) < 1)
158 return (-1);
159 }
160 #endif /* HAVE_POLL */
161
162 /*
163 * Read a side-channel message for the format:
164 *
165 * Byte(s) Description
166 * ------- -------------------------------------------
167 * 0 Command code
168 * 1 Status code
169 * 2-3 Data length (network byte order) <= 16384
170 * 4-N Data
171 */
172
173 while ((bytes = read(CUPS_SC_FD, buffer, sizeof(buffer))) < 0)
174 if (errno != EINTR && errno != EAGAIN)
175 return (-1);
176
177 /*
178 * Validate the command code in the message...
179 */
180
181 if (buffer[0] < CUPS_SC_CMD_SOFT_RESET || buffer[0] > CUPS_SC_CMD_GET_STATE)
182 return (-1);
183
184 *command = (cups_sc_command_t)buffer[0];
185
186 /*
187 * Validate the data length in the message...
188 */
189
190 templen = ((buffer[2] & 255) << 8) | (buffer[3] & 255);
191
192 if (templen > 0 && (!data || !datalen))
193 {
194 /*
195 * Either the response is bigger than the provided buffer or the
196 * response is bigger than we've read...
197 */
198
199 *status = CUPS_SC_STATUS_TOO_BIG;
200 }
201 else if (templen > *datalen || templen > (bytes - 4))
202 {
203 /*
204 * Either the response is bigger than the provided buffer or the
205 * response is bigger than we've read...
206 */
207
208 *status = CUPS_SC_STATUS_TOO_BIG;
209 }
210 else
211 {
212 /*
213 * The response data will fit, copy it over and provide the actual
214 * length...
215 */
216
217 *status = (cups_sc_status_t)buffer[1];
218 *datalen = templen;
219
220 memcpy(data, buffer + 4, templen);
221 }
222
223 return (0);
224 }
225
226
227 /*
228 * 'cupsSideChannelWrite()' - Write a side-channel message.
229 *
230 * This function is normally only called by backend programs to send
231 * responses to a filter, driver, or port monitor program.
232 *
233 * @since CUPS 1.3@
234 */
235
236 int /* O - 0 on success, -1 on error */
237 cupsSideChannelWrite(
238 cups_sc_command_t command, /* I - Command code */
239 cups_sc_status_t status, /* I - Status code */
240 const char *data, /* I - Data buffer pointer */
241 int datalen, /* I - Number of bytes of data */
242 double timeout) /* I - Timeout in seconds */
243 {
244 char buffer[16388]; /* Message buffer */
245 int bytes; /* Bytes written */
246 #ifdef HAVE_POLL
247 struct pollfd pfd; /* Poll structure for poll() */
248 #else /* select() */
249 fd_set output_set; /* Output set for select() */
250 struct timeval stimeout; /* Timeout value for select() */
251 #endif /* HAVE_POLL */
252
253
254 /*
255 * Range check input...
256 */
257
258 if (command < CUPS_SC_CMD_SOFT_RESET || command > CUPS_SC_CMD_GET_STATE ||
259 datalen < 0 || datalen > 16384 || (datalen > 0 && !data))
260 return (-1);
261
262 /*
263 * See if we can safely write to the side-channel socket...
264 */
265
266 #ifdef HAVE_POLL
267 pfd.fd = CUPS_SC_FD;
268 pfd.events = POLLOUT;
269
270 if (timeout < 0.0)
271 {
272 if (poll(&pfd, 1, -1) < 1)
273 return (-1);
274 }
275 else if (poll(&pfd, 1, (long)(timeout * 1000)) < 1)
276 return (-1);
277
278 #else /* select() */
279 FD_ZERO(&output_set);
280 FD_SET(CUPS_SC_FD, &output_set);
281
282 if (timeout < 0.0)
283 {
284 if (select(CUPS_SC_FD + 1, NULL, &output_set, NULL, NULL) < 1)
285 return (-1);
286 }
287 else
288 {
289 stimeout.tv_sec = (int)timeout;
290 stimeout.tv_usec = (int)(timeout * 1000000) % 1000000;
291
292 if (select(CUPS_SC_FD + 1, NULL, &output_set, NULL, &stimeout) < 1)
293 return (-1);
294 }
295 #endif /* HAVE_POLL */
296
297 /*
298 * Write a side-channel message in the format:
299 *
300 * Byte(s) Description
301 * ------- -------------------------------------------
302 * 0 Command code
303 * 1 Status code
304 * 2-3 Data length (network byte order) <= 16384
305 * 4-N Data
306 */
307
308 buffer[0] = command;
309 buffer[1] = status;
310 buffer[2] = datalen >> 8;
311 buffer[3] = datalen & 255;
312
313 bytes = 4;
314
315 if (datalen > 0)
316 {
317 memcpy(buffer + 4, data, datalen);
318 bytes += datalen;
319 }
320
321 while (write(CUPS_SC_FD, buffer, bytes) < 0)
322 if (errno != EINTR && errno != EAGAIN)
323 return (-1);
324
325 return (0);
326 }
327
328
329 /*
330 * End of "$Id: sidechannel.c 6649 2007-07-11 21:46:42Z mike $".
331 */