1 |
/* Licensed to the Apache Software Foundation (ASF) under one or more |
2 |
* contributor license agreements. See the NOTICE file distributed with |
3 |
* this work for additional information regarding copyright ownership. |
4 |
* The ASF licenses this file to You under the Apache License, Version 2.0 |
5 |
* (the "License"); you may not use this file except in compliance with |
6 |
* the License. You may obtain a copy of the License at |
7 |
* |
8 |
* http://www.apache.org/licenses/LICENSE-2.0 |
9 |
* |
10 |
* Unless required by applicable law or agreed to in writing, software |
11 |
* distributed under the License is distributed on an "AS IS" BASIS, |
12 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 |
* See the License for the specific language governing permissions and |
14 |
* limitations under the License. |
15 |
*/ |
16 |
|
17 |
#include "apr_arch_file_io.h" |
18 |
#include "apr_file_io.h" |
19 |
#include "apr_general.h" |
20 |
#include "apr_strings.h" |
21 |
#include "apr_escape.h" |
22 |
#if APR_HAVE_ERRNO_H |
23 |
#include <errno.h> |
24 |
#endif |
25 |
#include <string.h> |
26 |
#include <stdio.h> |
27 |
#if APR_HAVE_SYS_TYPES_H |
28 |
#include <sys/types.h> |
29 |
#endif |
30 |
#ifdef HAVE_SYS_STAT_H |
31 |
#include <sys/stat.h> |
32 |
#endif |
33 |
#if APR_HAVE_PROCESS_H |
34 |
#include <process.h> /* for getpid() on Win32 */ |
35 |
#endif |
36 |
#include "apr_arch_misc.h" |
37 |
|
38 |
APR_DECLARE(apr_status_t) apr_file_pipe_timeout_set(apr_file_t *thepipe, |
39 |
apr_interval_time_t timeout) |
40 |
{ |
41 |
/* Always OK to unset timeouts */ |
42 |
if (timeout == -1) { |
43 |
thepipe->timeout = timeout; |
44 |
return APR_SUCCESS; |
45 |
} |
46 |
if (!thepipe->pipe) { |
47 |
return APR_ENOTIMPL; |
48 |
} |
49 |
if (timeout && !(thepipe->pOverlapped)) { |
50 |
/* Cannot be nonzero if a pipe was opened blocking |
51 |
*/ |
52 |
return APR_EINVAL; |
53 |
} |
54 |
thepipe->timeout = timeout; |
55 |
return APR_SUCCESS; |
56 |
} |
57 |
|
58 |
APR_DECLARE(apr_status_t) apr_file_pipe_timeout_get(apr_file_t *thepipe, |
59 |
apr_interval_time_t *timeout) |
60 |
{ |
61 |
/* Always OK to get the timeout (even if it's unset ... -1) */ |
62 |
*timeout = thepipe->timeout; |
63 |
return APR_SUCCESS; |
64 |
} |
65 |
|
66 |
APR_DECLARE(apr_status_t) apr_file_pipe_create(apr_file_t **in, |
67 |
apr_file_t **out, |
68 |
apr_pool_t *p) |
69 |
{ |
70 |
/* Unix creates full blocking pipes. */ |
71 |
return apr_file_pipe_create_ex(in, out, APR_FULL_BLOCK, p); |
72 |
} |
73 |
|
74 |
APR_DECLARE(apr_status_t) apr_file_pipe_create_ex(apr_file_t **in, |
75 |
apr_file_t **out, |
76 |
apr_int32_t blocking, |
77 |
apr_pool_t *p) |
78 |
{ |
79 |
#ifdef _WIN32_WCE |
80 |
return APR_ENOTIMPL; |
81 |
#else |
82 |
SECURITY_ATTRIBUTES sa; |
83 |
static unsigned long id = 0; |
84 |
DWORD dwPipeMode; |
85 |
DWORD dwOpenMode; |
86 |
|
87 |
sa.nLength = sizeof(sa); |
88 |
|
89 |
#if APR_HAS_UNICODE_FS |
90 |
IF_WIN_OS_IS_UNICODE |
91 |
sa.bInheritHandle = FALSE; |
92 |
#endif |
93 |
#if APR_HAS_ANSI_FS |
94 |
ELSE_WIN_OS_IS_ANSI |
95 |
sa.bInheritHandle = TRUE; |
96 |
#endif |
97 |
sa.lpSecurityDescriptor = NULL; |
98 |
|
99 |
(*in) = (apr_file_t *)apr_pcalloc(p, sizeof(apr_file_t)); |
100 |
(*in)->pool = p; |
101 |
(*in)->fname = NULL; |
102 |
(*in)->pipe = 1; |
103 |
(*in)->timeout = -1; |
104 |
(*in)->ungetchar = -1; |
105 |
(*in)->eof_hit = 0; |
106 |
(*in)->filePtr = 0; |
107 |
(*in)->bufpos = 0; |
108 |
(*in)->dataRead = 0; |
109 |
(*in)->direction = 0; |
110 |
(*in)->pOverlapped = NULL; |
111 |
#if APR_FILES_AS_SOCKETS |
112 |
(void) apr_pollset_create(&(*in)->pollset, 1, p, 0); |
113 |
#endif |
114 |
(*out) = (apr_file_t *)apr_pcalloc(p, sizeof(apr_file_t)); |
115 |
(*out)->pool = p; |
116 |
(*out)->fname = NULL; |
117 |
(*out)->pipe = 1; |
118 |
(*out)->timeout = -1; |
119 |
(*out)->ungetchar = -1; |
120 |
(*out)->eof_hit = 0; |
121 |
(*out)->filePtr = 0; |
122 |
(*out)->bufpos = 0; |
123 |
(*out)->dataRead = 0; |
124 |
(*out)->direction = 0; |
125 |
(*out)->pOverlapped = NULL; |
126 |
#if APR_FILES_AS_SOCKETS |
127 |
(void) apr_pollset_create(&(*out)->pollset, 1, p, 0); |
128 |
#endif |
129 |
if (apr_os_level >= APR_WIN_NT) { |
130 |
char rand[8]; |
131 |
int pid = getpid(); |
132 |
#define FMT_PIPE_NAME "\\\\.\\pipe\\apr-pipe-%x.%lx." |
133 |
/* ^ ^ ^ |
134 |
* pid | | |
135 |
* | | |
136 |
* id | |
137 |
* | |
138 |
* hex-escaped rand[8] (16 bytes) |
139 |
*/ |
140 |
char name[sizeof FMT_PIPE_NAME + 2 * sizeof(pid) |
141 |
+ 2 * sizeof(id) |
142 |
+ 2 * sizeof(rand)]; |
143 |
apr_size_t pos; |
144 |
|
145 |
/* Create the read end of the pipe */ |
146 |
dwOpenMode = PIPE_ACCESS_INBOUND; |
147 |
#ifdef FILE_FLAG_FIRST_PIPE_INSTANCE |
148 |
dwOpenMode |= FILE_FLAG_FIRST_PIPE_INSTANCE; |
149 |
#endif |
150 |
if (blocking == APR_WRITE_BLOCK /* READ_NONBLOCK */ |
151 |
|| blocking == APR_FULL_NONBLOCK) { |
152 |
dwOpenMode |= FILE_FLAG_OVERLAPPED; |
153 |
(*in)->pOverlapped = (OVERLAPPED*) apr_pcalloc(p, sizeof(OVERLAPPED)); |
154 |
(*in)->pOverlapped->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); |
155 |
(*in)->timeout = 0; |
156 |
} |
157 |
dwPipeMode = 0; |
158 |
|
159 |
apr_generate_random_bytes(rand, sizeof rand); |
160 |
pos = apr_snprintf(name, sizeof name, FMT_PIPE_NAME, pid, id++); |
161 |
apr_escape_hex(name + pos, rand, sizeof rand, 0, NULL); |
162 |
|
163 |
(*in)->filehand = CreateNamedPipe(name, |
164 |
dwOpenMode, |
165 |
dwPipeMode, |
166 |
1, /* nMaxInstances, */ |
167 |
0, /* nOutBufferSize, */ |
168 |
65536, /* nInBufferSize, */ |
169 |
1, /* nDefaultTimeOut, */ |
170 |
&sa); |
171 |
if ((*in)->filehand == INVALID_HANDLE_VALUE) { |
172 |
apr_status_t rv = apr_get_os_error(); |
173 |
file_cleanup(*in); |
174 |
return rv; |
175 |
} |
176 |
|
177 |
/* Create the write end of the pipe */ |
178 |
dwOpenMode = FILE_ATTRIBUTE_NORMAL; |
179 |
if (blocking == APR_READ_BLOCK /* WRITE_NONBLOCK */ |
180 |
|| blocking == APR_FULL_NONBLOCK) { |
181 |
dwOpenMode |= FILE_FLAG_OVERLAPPED; |
182 |
(*out)->pOverlapped = (OVERLAPPED*) apr_pcalloc(p, sizeof(OVERLAPPED)); |
183 |
(*out)->pOverlapped->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); |
184 |
(*out)->timeout = 0; |
185 |
} |
186 |
|
187 |
(*out)->filehand = CreateFile(name, |
188 |
GENERIC_WRITE, /* access mode */ |
189 |
0, /* share mode */ |
190 |
&sa, /* Security attributes */ |
191 |
OPEN_EXISTING, /* dwCreationDisposition */ |
192 |
dwOpenMode, /* Pipe attributes */ |
193 |
NULL); /* handle to template file */ |
194 |
if ((*out)->filehand == INVALID_HANDLE_VALUE) { |
195 |
apr_status_t rv = apr_get_os_error(); |
196 |
file_cleanup(*out); |
197 |
file_cleanup(*in); |
198 |
return rv; |
199 |
} |
200 |
} |
201 |
else { |
202 |
/* Pipes on Win9* are blocking. Live with it. */ |
203 |
if (!CreatePipe(&(*in)->filehand, &(*out)->filehand, &sa, 65536)) { |
204 |
return apr_get_os_error(); |
205 |
} |
206 |
} |
207 |
|
208 |
apr_pool_cleanup_register((*in)->pool, (void *)(*in), file_cleanup, |
209 |
apr_pool_cleanup_null); |
210 |
apr_pool_cleanup_register((*out)->pool, (void *)(*out), file_cleanup, |
211 |
apr_pool_cleanup_null); |
212 |
return APR_SUCCESS; |
213 |
#endif /* _WIN32_WCE */ |
214 |
} |
215 |
|
216 |
|
217 |
APR_DECLARE(apr_status_t) apr_file_namedpipe_create(const char *filename, |
218 |
apr_fileperms_t perm, |
219 |
apr_pool_t *pool) |
220 |
{ |
221 |
/* Not yet implemented, interface not suitable. |
222 |
* Win32 requires the named pipe to be *opened* at the time it's |
223 |
* created, and to do so, blocking or non blocking must be elected. |
224 |
*/ |
225 |
return APR_ENOTIMPL; |
226 |
} |
227 |
|
228 |
|
229 |
/* XXX: Problem; we need to choose between blocking and nonblocking based |
230 |
* on how *thefile was opened, and we don't have that information :-/ |
231 |
* Hack; assume a blocking socket, since the most common use for the fn |
232 |
* would be to handle stdio-style or blocking pipes. Win32 doesn't have |
233 |
* select() blocking for pipes anyways :( |
234 |
*/ |
235 |
APR_DECLARE(apr_status_t) apr_os_pipe_put_ex(apr_file_t **file, |
236 |
apr_os_file_t *thefile, |
237 |
int register_cleanup, |
238 |
apr_pool_t *pool) |
239 |
{ |
240 |
(*file) = apr_pcalloc(pool, sizeof(apr_file_t)); |
241 |
(*file)->pool = pool; |
242 |
(*file)->pipe = 1; |
243 |
(*file)->timeout = -1; |
244 |
(*file)->ungetchar = -1; |
245 |
(*file)->filehand = *thefile; |
246 |
#if APR_FILES_AS_SOCKETS |
247 |
(void) apr_pollset_create(&(*file)->pollset, 1, pool, 0); |
248 |
#endif |
249 |
if (register_cleanup) { |
250 |
apr_pool_cleanup_register(pool, *file, file_cleanup, |
251 |
apr_pool_cleanup_null); |
252 |
} |
253 |
|
254 |
return APR_SUCCESS; |
255 |
} |
256 |
|
257 |
|
258 |
APR_DECLARE(apr_status_t) apr_os_pipe_put(apr_file_t **file, |
259 |
apr_os_file_t *thefile, |
260 |
apr_pool_t *pool) |
261 |
{ |
262 |
return apr_os_pipe_put_ex(file, thefile, 0, pool); |
263 |
} |
264 |
|
265 |
static apr_status_t create_socket_pipe(SOCKET *rd, SOCKET *wr) |
266 |
{ |
267 |
static int id = 0; |
268 |
FD_SET rs; |
269 |
SOCKET ls; |
270 |
struct timeval socktm; |
271 |
struct sockaddr_in pa; |
272 |
struct sockaddr_in la; |
273 |
struct sockaddr_in ca; |
274 |
int nrd; |
275 |
apr_status_t rv = APR_SUCCESS; |
276 |
int ll = sizeof(la); |
277 |
int lc = sizeof(ca); |
278 |
unsigned long bm = 1; |
279 |
int uid[2]; |
280 |
int iid[2]; |
281 |
|
282 |
*rd = INVALID_SOCKET; |
283 |
*wr = INVALID_SOCKET; |
284 |
|
285 |
/* Create the unique socket identifier |
286 |
* so that we know the connection originated |
287 |
* from us. |
288 |
*/ |
289 |
uid[0] = getpid(); |
290 |
uid[1] = id++; |
291 |
if ((ls = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) { |
292 |
return apr_get_netos_error(); |
293 |
} |
294 |
|
295 |
pa.sin_family = AF_INET; |
296 |
pa.sin_port = 0; |
297 |
pa.sin_addr.s_addr = inet_addr("127.0.0.1"); |
298 |
|
299 |
if (bind(ls, (SOCKADDR *)&pa, sizeof(pa)) == SOCKET_ERROR) { |
300 |
rv = apr_get_netos_error(); |
301 |
goto cleanup; |
302 |
} |
303 |
if (getsockname(ls, (SOCKADDR *)&la, &ll) == SOCKET_ERROR) { |
304 |
rv = apr_get_netos_error(); |
305 |
goto cleanup; |
306 |
} |
307 |
if (listen(ls, 1) == SOCKET_ERROR) { |
308 |
rv = apr_get_netos_error(); |
309 |
goto cleanup; |
310 |
} |
311 |
if ((*wr = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) { |
312 |
rv = apr_get_netos_error(); |
313 |
goto cleanup; |
314 |
} |
315 |
if (connect(*wr, (SOCKADDR *)&la, sizeof(la)) == SOCKET_ERROR) { |
316 |
rv = apr_get_netos_error(); |
317 |
goto cleanup; |
318 |
} |
319 |
if (send(*wr, (char *)uid, sizeof(uid), 0) != sizeof(uid)) { |
320 |
if ((rv = apr_get_netos_error()) == 0) { |
321 |
rv = APR_EINVAL; |
322 |
} |
323 |
goto cleanup; |
324 |
} |
325 |
if (ioctlsocket(ls, FIONBIO, &bm) == SOCKET_ERROR) { |
326 |
rv = apr_get_netos_error(); |
327 |
goto cleanup; |
328 |
} |
329 |
for (;;) { |
330 |
int ns; |
331 |
int nc = 0; |
332 |
/* Listening socket is nonblocking by now. |
333 |
* The accept should create the socket |
334 |
* immediatelly because we are connected already. |
335 |
* However on buys systems this can take a while |
336 |
* until winsock gets a chance to handle the events. |
337 |
*/ |
338 |
FD_ZERO(&rs); |
339 |
FD_SET(ls, &rs); |
340 |
|
341 |
socktm.tv_sec = 1; |
342 |
socktm.tv_usec = 0; |
343 |
if ((ns = select(0, &rs, NULL, NULL, &socktm)) == SOCKET_ERROR) { |
344 |
/* Accept still not signaled */ |
345 |
Sleep(100); |
346 |
continue; |
347 |
} |
348 |
if (ns == 0) { |
349 |
/* No connections in the last second */ |
350 |
continue; |
351 |
} |
352 |
if ((*rd = accept(ls, (SOCKADDR *)&ca, &lc)) == INVALID_SOCKET) { |
353 |
rv = apr_get_netos_error(); |
354 |
goto cleanup; |
355 |
} |
356 |
/* Verify the connection by reading the send identification. |
357 |
*/ |
358 |
do { |
359 |
if (nc++) |
360 |
Sleep(1); |
361 |
nrd = recv(*rd, (char *)iid, sizeof(iid), 0); |
362 |
rv = nrd == SOCKET_ERROR ? apr_get_netos_error() : APR_SUCCESS; |
363 |
} while (APR_STATUS_IS_EAGAIN(rv)); |
364 |
|
365 |
if (nrd == sizeof(iid)) { |
366 |
if (memcmp(uid, iid, sizeof(uid)) == 0) { |
367 |
/* Wow, we recived what we send. |
368 |
* Put read side of the pipe to the blocking |
369 |
* mode and return. |
370 |
*/ |
371 |
bm = 0; |
372 |
if (ioctlsocket(*rd, FIONBIO, &bm) == SOCKET_ERROR) { |
373 |
rv = apr_get_netos_error(); |
374 |
goto cleanup; |
375 |
} |
376 |
break; |
377 |
} |
378 |
} |
379 |
else if (nrd == SOCKET_ERROR) { |
380 |
goto cleanup; |
381 |
} |
382 |
closesocket(*rd); |
383 |
} |
384 |
/* We don't need the listening socket any more */ |
385 |
closesocket(ls); |
386 |
return 0; |
387 |
|
388 |
cleanup: |
389 |
/* Don't leak resources */ |
390 |
if (*rd != INVALID_SOCKET) |
391 |
closesocket(*rd); |
392 |
if (*wr != INVALID_SOCKET) |
393 |
closesocket(*wr); |
394 |
|
395 |
*rd = INVALID_SOCKET; |
396 |
*wr = INVALID_SOCKET; |
397 |
closesocket(ls); |
398 |
return rv; |
399 |
} |
400 |
|
401 |
static apr_status_t socket_pipe_cleanup(void *thefile) |
402 |
{ |
403 |
apr_file_t *file = thefile; |
404 |
if (file->filehand != INVALID_HANDLE_VALUE) { |
405 |
shutdown((SOCKET)file->filehand, SD_BOTH); |
406 |
closesocket((SOCKET)file->filehand); |
407 |
file->filehand = INVALID_HANDLE_VALUE; |
408 |
} |
409 |
return APR_SUCCESS; |
410 |
} |
411 |
|
412 |
apr_status_t apr_file_socket_pipe_create(apr_file_t **in, |
413 |
apr_file_t **out, |
414 |
apr_pool_t *p) |
415 |
{ |
416 |
apr_status_t rv; |
417 |
SOCKET rd; |
418 |
SOCKET wr; |
419 |
|
420 |
if ((rv = create_socket_pipe(&rd, &wr)) != APR_SUCCESS) { |
421 |
return rv; |
422 |
} |
423 |
(*in) = (apr_file_t *)apr_pcalloc(p, sizeof(apr_file_t)); |
424 |
(*in)->pool = p; |
425 |
(*in)->fname = NULL; |
426 |
(*in)->pipe = 1; |
427 |
(*in)->timeout = -1; |
428 |
(*in)->ungetchar = -1; |
429 |
(*in)->eof_hit = 0; |
430 |
(*in)->filePtr = 0; |
431 |
(*in)->bufpos = 0; |
432 |
(*in)->dataRead = 0; |
433 |
(*in)->direction = 0; |
434 |
(*in)->pOverlapped = (OVERLAPPED*)apr_pcalloc(p, sizeof(OVERLAPPED)); |
435 |
(*in)->filehand = (HANDLE)rd; |
436 |
|
437 |
(*out) = (apr_file_t *)apr_pcalloc(p, sizeof(apr_file_t)); |
438 |
(*out)->pool = p; |
439 |
(*out)->fname = NULL; |
440 |
(*out)->pipe = 1; |
441 |
(*out)->timeout = -1; |
442 |
(*out)->ungetchar = -1; |
443 |
(*out)->eof_hit = 0; |
444 |
(*out)->filePtr = 0; |
445 |
(*out)->bufpos = 0; |
446 |
(*out)->dataRead = 0; |
447 |
(*out)->direction = 0; |
448 |
(*out)->pOverlapped = (OVERLAPPED*)apr_pcalloc(p, sizeof(OVERLAPPED)); |
449 |
(*out)->filehand = (HANDLE)wr; |
450 |
|
451 |
apr_pool_cleanup_register(p, (void *)(*in), socket_pipe_cleanup, |
452 |
apr_pool_cleanup_null); |
453 |
apr_pool_cleanup_register(p, (void *)(*out), socket_pipe_cleanup, |
454 |
apr_pool_cleanup_null); |
455 |
|
456 |
return rv; |
457 |
} |
458 |
|
459 |
apr_status_t apr_file_socket_pipe_close(apr_file_t *file) |
460 |
{ |
461 |
apr_status_t stat; |
462 |
if (!file->pipe) |
463 |
return apr_file_close(file); |
464 |
if ((stat = socket_pipe_cleanup(file)) == APR_SUCCESS) { |
465 |
apr_pool_cleanup_kill(file->pool, file, socket_pipe_cleanup); |
466 |
|
467 |
if (file->mutex) { |
468 |
apr_thread_mutex_destroy(file->mutex); |
469 |
} |
470 |
|
471 |
return APR_SUCCESS; |
472 |
} |
473 |
return stat; |
474 |
} |
475 |
|