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