VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/posix/pipe-posix.cpp@ 27721

Last change on this file since 27721 was 27721, checked in by vboxsync, 15 years ago

RTPipeFromNative/posix: Solaris creates bi-directional pipes so we have to allow for O_RDWR too.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.7 KB
Line 
1/* $Id: pipe-posix.cpp 27721 2010-03-25 21:41:33Z vboxsync $ */
2/** @file
3 * IPRT - Anonymous Pipes, POSIX Implementation.
4 */
5
6/*
7 * Copyright (C) 2010 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 *
26 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
27 * Clara, CA 95054 USA or visit http://www.sun.com if you need
28 * additional information or have any questions.
29 */
30
31
32/*******************************************************************************
33* Header Files *
34*******************************************************************************/
35#include <iprt/pipe.h>
36#include "internal/iprt.h"
37
38#include <iprt/asm.h>
39#include <iprt/assert.h>
40#include <iprt/err.h>
41#include <iprt/mem.h>
42#include <iprt/string.h>
43#include <iprt/thread.h>
44#include "internal/magics.h"
45
46#include <errno.h>
47#include <fcntl.h>
48#include <limits.h>
49#include <unistd.h>
50#include <sys/poll.h>
51#include <sys/stat.h>
52#include <signal.h>
53
54
55/*******************************************************************************
56* Structures and Typedefs *
57*******************************************************************************/
58typedef struct RTPIPEINTERNAL
59{
60 /** Magic value (RTPIPE_MAGIC). */
61 uint32_t u32Magic;
62 /** The file descriptor. */
63 int fd;
64 /** Set if this is the read end, clear if it's the write end. */
65 bool fRead;
66 /** Atomically operated state variable.
67 *
68 * - Bits 0 thru 29 - Users of the new mode.
69 * - Bit 30 - The pipe mode, set indicates blocking.
70 * - Bit 31 - Set when we're switching the mode.
71 */
72 uint32_t volatile u32State;
73} RTPIPEINTERNAL;
74
75
76/*******************************************************************************
77* Defined Constants And Macros *
78*******************************************************************************/
79/** @name RTPIPEINTERNAL::u32State defines
80 * @{ */
81#define RTPIPE_POSIX_BLOCKING UINT32_C(0x40000000)
82#define RTPIPE_POSIX_SWITCHING UINT32_C(0x80000000)
83#define RTPIPE_POSIX_SWITCHING_BIT 31
84#define RTPIPE_POSIX_USERS_MASK UINT32_C(0x3fffffff)
85/** @} */
86
87
88RTDECL(int) RTPipeCreate(PRTPIPE phPipeRead, PRTPIPE phPipeWrite, uint32_t fFlags)
89{
90 AssertPtrReturn(phPipeRead, VERR_INVALID_POINTER);
91 AssertPtrReturn(phPipeWrite, VERR_INVALID_POINTER);
92 AssertReturn(!(fFlags & ~RTPIPE_C_VALID_MASK), VERR_INVALID_PARAMETER);
93
94 /*
95 * Create the pipe and set the close-on-exec flag if requested.
96 */
97 int aFds[2] = {-1, -1};
98 if (pipe(aFds))
99 return RTErrConvertFromErrno(errno);
100
101 int rc = VINF_SUCCESS;
102 if (!(fFlags & RTPIPE_C_INHERIT_READ))
103 {
104 if (fcntl(aFds[0], F_SETFD, FD_CLOEXEC))
105 rc = RTErrConvertFromErrno(errno);
106 }
107
108 if (!(fFlags & RTPIPE_C_INHERIT_WRITE))
109 {
110 if (fcntl(aFds[1], F_SETFD, FD_CLOEXEC))
111 rc = RTErrConvertFromErrno(errno);
112 }
113
114 if (RT_SUCCESS(rc))
115 {
116 /*
117 * Create the two handles.
118 */
119 RTPIPEINTERNAL *pThisR = (RTPIPEINTERNAL *)RTMemAlloc(sizeof(RTPIPEINTERNAL));
120 if (pThisR)
121 {
122 RTPIPEINTERNAL *pThisW = (RTPIPEINTERNAL *)RTMemAlloc(sizeof(RTPIPEINTERNAL));
123 if (pThisW)
124 {
125 pThisR->u32Magic = RTPIPE_MAGIC;
126 pThisW->u32Magic = RTPIPE_MAGIC;
127 pThisR->fd = aFds[0];
128 pThisW->fd = aFds[1];
129 pThisR->fRead = true;
130 pThisW->fRead = false;
131 pThisR->u32State = RTPIPE_POSIX_BLOCKING;
132 pThisW->u32State = RTPIPE_POSIX_BLOCKING;
133
134 *phPipeRead = pThisR;
135 *phPipeWrite = pThisW;
136
137 /*
138 * Before we leave, make sure to shut up SIGPIPE.
139 */
140 signal(SIGPIPE, SIG_IGN);
141 return VINF_SUCCESS;
142 }
143
144 RTMemFree(pThisR);
145 rc = VERR_NO_MEMORY;
146 }
147 else
148 rc = VERR_NO_MEMORY;
149 }
150
151 close(aFds[0]);
152 close(aFds[1]);
153 return rc;
154}
155
156
157RTDECL(int) RTPipeClose(RTPIPE hPipe)
158{
159 RTPIPEINTERNAL *pThis = hPipe;
160 if (pThis == NIL_RTPIPE)
161 return VINF_SUCCESS;
162 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
163 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
164
165 /*
166 * Do the cleanup.
167 */
168 AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, ~RTPIPE_MAGIC, RTPIPE_MAGIC), VERR_INVALID_HANDLE);
169
170 int fd = pThis->fd;
171 pThis->fd = -1;
172 close(fd);
173
174 if (ASMAtomicReadU32(&pThis->u32State) & RTPIPE_POSIX_USERS_MASK)
175 {
176 AssertFailed();
177 RTThreadSleep(1);
178 }
179
180 RTMemFree(pThis);
181
182 return VINF_SUCCESS;
183}
184
185RTDECL(int) RTPipeFromNative(PRTPIPE phPipe, RTHCINTPTR hNativePipe, uint32_t fFlags)
186{
187 AssertPtrReturn(phPipe, VERR_INVALID_POINTER);
188 AssertReturn(!(fFlags & ~RTPIPE_N_VALID_MASK), VERR_INVALID_PARAMETER);
189 AssertReturn(!!(fFlags & RTPIPE_N_READ) != !!(fFlags & RTPIPE_N_WRITE), VERR_INVALID_PARAMETER);
190
191 /*
192 * Get and validate the pipe handle info.
193 */
194 int hNative = (int)hNativePipe;
195 struct stat st;
196 AssertReturn(fstat(hNative, &st) == 0, RTErrConvertFromErrno(errno));
197 AssertMsgReturn(S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode), ("%#x (%o)\n", st.st_mode, st.st_mode), VERR_INVALID_HANDLE);
198
199 int fFd = fcntl(hNative, F_GETFL, 0);
200 AssertReturn(fFd != -1, VERR_INVALID_HANDLE);
201 AssertMsgReturn( (fFd & O_ACCMODE) == (fFlags & RTPIPE_N_READ ? O_RDONLY : O_WRONLY)
202 || (fFd & O_ACCMODE) == O_RDWR /* Solaris creates bi-directional pipes. */
203 , ("%#x\n", fFd), VERR_INVALID_HANDLE);
204
205 /*
206 * Create the handle.
207 */
208 RTPIPEINTERNAL *pThis = (RTPIPEINTERNAL *)RTMemAlloc(sizeof(RTPIPEINTERNAL));
209 if (!pThis)
210 return VERR_NO_MEMORY;
211
212 pThis->u32Magic = RTPIPE_MAGIC;
213 pThis->fd = hNative;
214 pThis->fRead = !!(fFlags & RTPIPE_N_READ);
215 pThis->u32State = fFd & O_NONBLOCK ? 0 : RTPIPE_POSIX_BLOCKING;
216
217 /*
218 * Fix up inheritability and shut up SIGPIPE and we're done.
219 */
220 if (fcntl(hNative, F_SETFD, fFlags & RTPIPE_N_INHERIT ? 0 : FD_CLOEXEC) == 0)
221 {
222 signal(SIGPIPE, SIG_IGN);
223 *phPipe = pThis;
224 return VINF_SUCCESS;
225 }
226
227 int rc = RTErrConvertFromErrno(errno);
228 RTMemFree(pThis);
229 return rc;
230}
231
232
233RTDECL(RTHCINTPTR) RTPipeToNative(RTPIPE hPipe)
234{
235 RTPIPEINTERNAL *pThis = hPipe;
236 AssertPtrReturn(pThis, -1);
237 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, -1);
238
239 return pThis->fd;
240}
241
242
243/**
244 * Prepare blocking mode.
245 *
246 * @returns VINF_SUCCESS
247 * @retval VERR_WRONG_ORDER
248 * @retval VERR_INTERNAL_ERROR_4
249 *
250 * @param pThis The pipe handle.
251 */
252static int rtPipeTryBlocking(RTPIPEINTERNAL *pThis)
253{
254 /*
255 * Update the state.
256 */
257 for (;;)
258 {
259 uint32_t u32State = ASMAtomicReadU32(&pThis->u32State);
260 uint32_t const u32StateOld = u32State;
261 uint32_t const cUsers = (u32State & RTPIPE_POSIX_USERS_MASK);
262
263 if (u32State & RTPIPE_POSIX_BLOCKING)
264 {
265 AssertReturn(cUsers < RTPIPE_POSIX_USERS_MASK / 2, VERR_INTERNAL_ERROR_4);
266 u32State &= ~RTPIPE_POSIX_USERS_MASK;
267 u32State |= cUsers + 1;
268 if (ASMAtomicCmpXchgU32(&pThis->u32State, u32State, u32StateOld))
269 {
270 if (u32State & RTPIPE_POSIX_SWITCHING)
271 break;
272 return VINF_SUCCESS;
273 }
274 }
275 else if (cUsers == 0)
276 {
277 u32State = 1 | RTPIPE_POSIX_SWITCHING | RTPIPE_POSIX_BLOCKING;
278 if (ASMAtomicCmpXchgU32(&pThis->u32State, u32State, u32StateOld))
279 break;
280 }
281 else
282 return VERR_WRONG_ORDER;
283 ASMNopPause();
284 }
285
286 /*
287 * Do the switching.
288 */
289 int fFlags = fcntl(pThis->fd, F_GETFL, 0);
290 if (fFlags != -1)
291 {
292 if ( !(fFlags & O_NONBLOCK)
293 || fcntl(pThis->fd, F_SETFL, fFlags & ~O_NONBLOCK) != -1)
294 {
295 ASMAtomicBitClear(&pThis->u32State, RTPIPE_POSIX_SWITCHING_BIT);
296 return VINF_SUCCESS;
297 }
298 }
299
300 ASMAtomicDecU32(&pThis->u32State);
301 return RTErrConvertFromErrno(errno);
302}
303
304
305/**
306 * Prepare non-blocking mode.
307 *
308 * @returns VINF_SUCCESS
309 * @retval VERR_WRONG_ORDER
310 * @retval VERR_INTERNAL_ERROR_4
311 *
312 * @param pThis The pipe handle.
313 */
314static int rtPipeTryNonBlocking(RTPIPEINTERNAL *pThis)
315{
316 /*
317 * Update the state.
318 */
319 for (;;)
320 {
321 uint32_t u32State = ASMAtomicReadU32(&pThis->u32State);
322 uint32_t const u32StateOld = u32State;
323 uint32_t const cUsers = (u32State & RTPIPE_POSIX_USERS_MASK);
324
325 if (!(u32State & RTPIPE_POSIX_BLOCKING))
326 {
327 AssertReturn(cUsers < RTPIPE_POSIX_USERS_MASK / 2, VERR_INTERNAL_ERROR_4);
328 u32State &= ~RTPIPE_POSIX_USERS_MASK;
329 u32State |= cUsers + 1;
330 if (ASMAtomicCmpXchgU32(&pThis->u32State, u32State, u32StateOld))
331 {
332 if (u32State & RTPIPE_POSIX_SWITCHING)
333 break;
334 return VINF_SUCCESS;
335 }
336 }
337 else if (cUsers == 0)
338 {
339 u32State = 1 | RTPIPE_POSIX_SWITCHING;
340 if (ASMAtomicCmpXchgU32(&pThis->u32State, u32State, u32StateOld))
341 break;
342 }
343 else
344 return VERR_WRONG_ORDER;
345 ASMNopPause();
346 }
347
348 /*
349 * Do the switching.
350 */
351 int fFlags = fcntl(pThis->fd, F_GETFL, 0);
352 if (fFlags != -1)
353 {
354 if ( (fFlags & O_NONBLOCK)
355 || fcntl(pThis->fd, F_SETFL, fFlags | O_NONBLOCK) != -1)
356 {
357 ASMAtomicBitClear(&pThis->u32State, RTPIPE_POSIX_SWITCHING_BIT);
358 return VINF_SUCCESS;
359 }
360 }
361
362 ASMAtomicDecU32(&pThis->u32State);
363 return RTErrConvertFromErrno(errno);
364}
365
366
367/**
368 * Checks if the read pipe has a HUP condition.
369 *
370 * @returns true if HUP, false if no.
371 * @param pThis The pipe handle (read).
372 */
373static bool rtPipePosixHasHup(RTPIPEINTERNAL *pThis)
374{
375 Assert(pThis->fRead);
376
377 struct pollfd PollFd;
378 RT_ZERO(PollFd);
379 PollFd.fd = pThis->fd;
380 PollFd.events = POLLHUP;
381 return poll(&PollFd, 1, 0) >= 1
382 && (PollFd.revents & POLLHUP);
383}
384
385
386RTDECL(int) RTPipeRead(RTPIPE hPipe, void *pvBuf, size_t cbToRead, size_t *pcbRead)
387{
388 RTPIPEINTERNAL *pThis = hPipe;
389 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
390 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
391 AssertReturn(pThis->fRead, VERR_ACCESS_DENIED);
392 AssertPtr(pcbRead);
393 AssertPtr(pvBuf);
394
395 int rc = rtPipeTryNonBlocking(pThis);
396 if (RT_SUCCESS(rc))
397 {
398 ssize_t cbRead = read(pThis->fd, pvBuf, RT_MIN(cbToRead, SSIZE_MAX));
399 if (cbRead >= 0)
400 {
401 if (cbRead || !cbToRead || !rtPipePosixHasHup(pThis))
402 *pcbRead = cbRead;
403 else
404 rc = VERR_BROKEN_PIPE;
405 }
406 else if (errno == EAGAIN)
407 {
408 *pcbRead = 0;
409 rc = VINF_TRY_AGAIN;
410 }
411 else
412 rc = RTErrConvertFromErrno(errno);
413
414 ASMAtomicDecU32(&pThis->u32State);
415 }
416 return rc;
417}
418
419
420RTDECL(int) RTPipeReadBlocking(RTPIPE hPipe, void *pvBuf, size_t cbToRead, size_t *pcbRead)
421{
422 RTPIPEINTERNAL *pThis = hPipe;
423 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
424 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
425 AssertReturn(pThis->fRead, VERR_ACCESS_DENIED);
426 AssertPtr(pvBuf);
427
428 int rc = rtPipeTryBlocking(pThis);
429 if (RT_SUCCESS(rc))
430 {
431 size_t cbTotalRead = 0;;
432 while (cbToRead > 0)
433 {
434 ssize_t cbRead = read(pThis->fd, pvBuf, RT_MIN(cbToRead, SSIZE_MAX));
435 if (cbRead < 0)
436 {
437 rc = RTErrConvertFromErrno(errno);
438 break;
439 }
440 if (!cbRead && cbToRead > 0 && rtPipePosixHasHup(pThis))
441 {
442 rc = VERR_BROKEN_PIPE;
443 break;
444 }
445
446 /* advance */
447 pvBuf = (char *)pvBuf + cbRead;
448 cbTotalRead += cbRead;
449 cbToRead -= cbRead;
450 }
451
452 if (pcbRead)
453 {
454 *pcbRead = cbTotalRead;
455 if ( RT_FAILURE(rc)
456 && cbTotalRead
457 && rc != VERR_INVALID_POINTER)
458 rc = VINF_SUCCESS;
459 }
460
461 ASMAtomicDecU32(&pThis->u32State);
462 }
463 return rc;
464}
465
466
467RTDECL(int) RTPipeWrite(RTPIPE hPipe, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
468{
469 RTPIPEINTERNAL *pThis = hPipe;
470 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
471 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
472 AssertReturn(!pThis->fRead, VERR_ACCESS_DENIED);
473 AssertPtr(pcbWritten);
474 AssertPtr(pvBuf);
475
476 int rc = rtPipeTryNonBlocking(pThis);
477 if (RT_SUCCESS(rc))
478 {
479 if (cbToWrite)
480 {
481 ssize_t cbWritten = write(pThis->fd, pvBuf, RT_MIN(cbToWrite, SSIZE_MAX));
482 if (cbWritten >= 0)
483 *pcbWritten = cbWritten;
484 else if (errno == EAGAIN)
485 {
486 *pcbWritten = 0;
487 rc = VINF_TRY_AGAIN;
488 }
489 else
490 rc = RTErrConvertFromErrno(errno);
491 }
492 else
493 *pcbWritten = 0;
494
495 ASMAtomicDecU32(&pThis->u32State);
496 }
497 return rc;
498}
499
500
501RTDECL(int) RTPipeWriteBlocking(RTPIPE hPipe, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
502{
503 RTPIPEINTERNAL *pThis = hPipe;
504 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
505 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
506 AssertReturn(!pThis->fRead, VERR_ACCESS_DENIED);
507 AssertPtr(pvBuf);
508 AssertPtrNull(pcbWritten);
509
510 int rc = rtPipeTryBlocking(pThis);
511 if (RT_SUCCESS(rc))
512 {
513 size_t cbTotalWritten = 0;
514 while (cbToWrite > 0)
515 {
516 ssize_t cbWritten = write(pThis->fd, pvBuf, RT_MIN(cbToWrite, SSIZE_MAX));
517 if (cbWritten < 0)
518 {
519 rc = RTErrConvertFromErrno(errno);
520 break;
521 }
522
523 /* advance */
524 pvBuf = (char const *)pvBuf + cbWritten;
525 cbTotalWritten += cbWritten;
526 cbToWrite -= cbWritten;
527 }
528
529 if (pcbWritten)
530 {
531 *pcbWritten = cbTotalWritten;
532 if ( RT_FAILURE(rc)
533 && cbTotalWritten
534 && rc != VERR_INVALID_POINTER)
535 rc = VINF_SUCCESS;
536 }
537
538 ASMAtomicDecU32(&pThis->u32State);
539 }
540 return rc;
541}
542
543
544RTDECL(int) RTPipeFlush(RTPIPE hPipe)
545{
546 RTPIPEINTERNAL *pThis = hPipe;
547 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
548 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
549 AssertReturn(!pThis->fRead, VERR_ACCESS_DENIED);
550
551 if (fsync(pThis->fd))
552 {
553 if (errno == EINVAL || errno == ENOTSUP)
554 return VERR_NOT_SUPPORTED;
555 return RTErrConvertFromErrno(errno);
556 }
557 return VINF_SUCCESS;
558}
559
560
561RTDECL(int) RTPipeSelectOne(RTPIPE hPipe, RTMSINTERVAL cMillies)
562{
563 RTPIPEINTERNAL *pThis = hPipe;
564 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
565 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
566
567 struct pollfd PollFd;
568 RT_ZERO(PollFd);
569 PollFd.fd = pThis->fd;
570 PollFd.events = POLLHUP | POLLERR;
571 if (pThis->fRead)
572 PollFd.events |= POLLIN | POLLPRI;
573 else
574 PollFd.events |= POLLOUT;
575
576 int timeout;
577 if ( cMillies == RT_INDEFINITE_WAIT
578 || cMillies >= INT_MAX /* lazy bird */)
579 timeout = -1;
580 else
581 timeout = cMillies;
582
583 int rc = poll(&PollFd, 1, 0);
584 if (rc == -1)
585 return RTErrConvertFromErrno(errno);
586 return rc > 0 ? VINF_SUCCESS : VERR_TIMEOUT;
587}
588
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette