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