VirtualBox

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

Last change on this file since 39690 was 39690, checked in by vboxsync, 13 years ago

IPRT: Added RTPipeQueryReadable.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.6 KB
Line 
1/* $Id: pipe-posix.cpp 39690 2011-12-30 13:06:50Z vboxsync $ */
2/** @file
3 * IPRT - Anonymous Pipes, POSIX Implementation.
4 */
5
6/*
7 * Copyright (C) 2010 Oracle Corporation
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
27
28/*******************************************************************************
29* Header Files *
30*******************************************************************************/
31#include <iprt/pipe.h>
32#include "internal/iprt.h"
33
34#include <iprt/asm.h>
35#include <iprt/assert.h>
36#include <iprt/err.h>
37#include <iprt/mem.h>
38#include <iprt/string.h>
39#include <iprt/thread.h>
40#include "internal/magics.h"
41
42#include <errno.h>
43#include <fcntl.h>
44#include <limits.h>
45#include <unistd.h>
46#include <sys/ioctl.h>
47#include <sys/poll.h>
48#include <sys/stat.h>
49#include <signal.h>
50#ifdef RT_OS_LINUX
51# include <sys/syscall.h>
52#endif
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
88
89/**
90 * Wrapper for calling pipe2() or pipe().
91 *
92 * When using pipe2() the returned handles are marked close-on-exec and does
93 * not risk racing process creation calls on other threads.
94 *
95 * @returns See pipe().
96 * @param paFds See pipe().
97 * @param piNewPipeSyscall Where to cache which call we should used. -1 if
98 * pipe(), 1 if pipe2(), 0 if not yet decided.
99 */
100static int my_pipe_wrapper(int *paFds, int *piNewPipeSyscall)
101{
102 if (*piNewPipeSyscall >= 0)
103 {
104#if defined(RT_OS_LINUX) && defined(__NR_pipe2) && defined(O_CLOEXEC)
105 long rc = syscall(__NR_pipe2, paFds, O_CLOEXEC);
106 if (rc >= 0)
107 {
108 if (*piNewPipeSyscall == 0)
109 *piNewPipeSyscall = 1;
110 return (int)rc;
111 }
112#endif
113 *piNewPipeSyscall = -1;
114 }
115
116 return pipe(paFds);
117}
118
119
120RTDECL(int) RTPipeCreate(PRTPIPE phPipeRead, PRTPIPE phPipeWrite, uint32_t fFlags)
121{
122 AssertPtrReturn(phPipeRead, VERR_INVALID_POINTER);
123 AssertPtrReturn(phPipeWrite, VERR_INVALID_POINTER);
124 AssertReturn(!(fFlags & ~RTPIPE_C_VALID_MASK), VERR_INVALID_PARAMETER);
125
126 /*
127 * Create the pipe and clear/set the close-on-exec flag as required.
128 */
129 int aFds[2] = {-1, -1};
130 static int s_iNewPipeSyscall = 0;
131 if (my_pipe_wrapper(aFds, &s_iNewPipeSyscall))
132 return RTErrConvertFromErrno(errno);
133
134 int rc = VINF_SUCCESS;
135 if (s_iNewPipeSyscall > 0)
136 {
137 /* created with close-on-exec set. */
138 if (fFlags & RTPIPE_C_INHERIT_READ)
139 {
140 if (fcntl(aFds[0], F_SETFD, 0))
141 rc = RTErrConvertFromErrno(errno);
142 }
143
144 if (fFlags & RTPIPE_C_INHERIT_WRITE)
145 {
146 if (fcntl(aFds[1], F_SETFD, 0))
147 rc = RTErrConvertFromErrno(errno);
148 }
149 }
150 else
151 {
152 /* created with close-on-exec cleared. */
153 if (!(fFlags & RTPIPE_C_INHERIT_READ))
154 {
155 if (fcntl(aFds[0], F_SETFD, FD_CLOEXEC))
156 rc = RTErrConvertFromErrno(errno);
157 }
158
159 if (!(fFlags & RTPIPE_C_INHERIT_WRITE))
160 {
161 if (fcntl(aFds[1], F_SETFD, FD_CLOEXEC))
162 rc = RTErrConvertFromErrno(errno);
163 }
164 }
165
166 if (RT_SUCCESS(rc))
167 {
168 /*
169 * Create the two handles.
170 */
171 RTPIPEINTERNAL *pThisR = (RTPIPEINTERNAL *)RTMemAlloc(sizeof(RTPIPEINTERNAL));
172 if (pThisR)
173 {
174 RTPIPEINTERNAL *pThisW = (RTPIPEINTERNAL *)RTMemAlloc(sizeof(RTPIPEINTERNAL));
175 if (pThisW)
176 {
177 pThisR->u32Magic = RTPIPE_MAGIC;
178 pThisW->u32Magic = RTPIPE_MAGIC;
179 pThisR->fd = aFds[0];
180 pThisW->fd = aFds[1];
181 pThisR->fRead = true;
182 pThisW->fRead = false;
183 pThisR->u32State = RTPIPE_POSIX_BLOCKING;
184 pThisW->u32State = RTPIPE_POSIX_BLOCKING;
185
186 *phPipeRead = pThisR;
187 *phPipeWrite = pThisW;
188
189 /*
190 * Before we leave, make sure to shut up SIGPIPE.
191 */
192 signal(SIGPIPE, SIG_IGN);
193 return VINF_SUCCESS;
194 }
195
196 RTMemFree(pThisR);
197 rc = VERR_NO_MEMORY;
198 }
199 else
200 rc = VERR_NO_MEMORY;
201 }
202
203 close(aFds[0]);
204 close(aFds[1]);
205 return rc;
206}
207
208
209RTDECL(int) RTPipeClose(RTPIPE hPipe)
210{
211 RTPIPEINTERNAL *pThis = hPipe;
212 if (pThis == NIL_RTPIPE)
213 return VINF_SUCCESS;
214 AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
215 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
216
217 /*
218 * Do the cleanup.
219 */
220 AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, ~RTPIPE_MAGIC, RTPIPE_MAGIC), VERR_INVALID_HANDLE);
221
222 int fd = pThis->fd;
223 pThis->fd = -1;
224 close(fd);
225
226 if (ASMAtomicReadU32(&pThis->u32State) & RTPIPE_POSIX_USERS_MASK)
227 {
228 AssertFailed();
229 RTThreadSleep(1);
230 }
231
232 RTMemFree(pThis);
233
234 return VINF_SUCCESS;
235}
236
237RTDECL(int) RTPipeFromNative(PRTPIPE phPipe, RTHCINTPTR hNativePipe, uint32_t fFlags)
238{
239 AssertPtrReturn(phPipe, VERR_INVALID_POINTER);
240 AssertReturn(!(fFlags & ~RTPIPE_N_VALID_MASK), VERR_INVALID_PARAMETER);
241 AssertReturn(!!(fFlags & RTPIPE_N_READ) != !!(fFlags & RTPIPE_N_WRITE), VERR_INVALID_PARAMETER);
242
243 /*
244 * Get and validate the pipe handle info.
245 */
246 int hNative = (int)hNativePipe;
247 struct stat st;
248 AssertReturn(fstat(hNative, &st) == 0, RTErrConvertFromErrno(errno));
249 AssertMsgReturn(S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode), ("%#x (%o)\n", st.st_mode, st.st_mode), VERR_INVALID_HANDLE);
250
251 int fFd = fcntl(hNative, F_GETFL, 0);
252 AssertReturn(fFd != -1, VERR_INVALID_HANDLE);
253 AssertMsgReturn( (fFd & O_ACCMODE) == (fFlags & RTPIPE_N_READ ? O_RDONLY : O_WRONLY)
254 || (fFd & O_ACCMODE) == O_RDWR /* Solaris creates bi-directional pipes. */
255 , ("%#x\n", fFd), VERR_INVALID_HANDLE);
256
257 /*
258 * Create the handle.
259 */
260 RTPIPEINTERNAL *pThis = (RTPIPEINTERNAL *)RTMemAlloc(sizeof(RTPIPEINTERNAL));
261 if (!pThis)
262 return VERR_NO_MEMORY;
263
264 pThis->u32Magic = RTPIPE_MAGIC;
265 pThis->fd = hNative;
266 pThis->fRead = !!(fFlags & RTPIPE_N_READ);
267 pThis->u32State = fFd & O_NONBLOCK ? 0 : RTPIPE_POSIX_BLOCKING;
268
269 /*
270 * Fix up inheritability and shut up SIGPIPE and we're done.
271 */
272 if (fcntl(hNative, F_SETFD, fFlags & RTPIPE_N_INHERIT ? 0 : FD_CLOEXEC) == 0)
273 {
274 signal(SIGPIPE, SIG_IGN);
275 *phPipe = pThis;
276 return VINF_SUCCESS;
277 }
278
279 int rc = RTErrConvertFromErrno(errno);
280 RTMemFree(pThis);
281 return rc;
282}
283
284
285RTDECL(RTHCINTPTR) RTPipeToNative(RTPIPE hPipe)
286{
287 RTPIPEINTERNAL *pThis = hPipe;
288 AssertPtrReturn(pThis, -1);
289 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, -1);
290
291 return pThis->fd;
292}
293
294
295/**
296 * Prepare blocking mode.
297 *
298 * @returns VINF_SUCCESS
299 * @retval VERR_WRONG_ORDER
300 * @retval VERR_INTERNAL_ERROR_4
301 *
302 * @param pThis The pipe handle.
303 */
304static int rtPipeTryBlocking(RTPIPEINTERNAL *pThis)
305{
306 /*
307 * Update the state.
308 */
309 for (;;)
310 {
311 uint32_t u32State = ASMAtomicReadU32(&pThis->u32State);
312 uint32_t const u32StateOld = u32State;
313 uint32_t const cUsers = (u32State & RTPIPE_POSIX_USERS_MASK);
314
315 if (u32State & RTPIPE_POSIX_BLOCKING)
316 {
317 AssertReturn(cUsers < RTPIPE_POSIX_USERS_MASK / 2, VERR_INTERNAL_ERROR_4);
318 u32State &= ~RTPIPE_POSIX_USERS_MASK;
319 u32State |= cUsers + 1;
320 if (ASMAtomicCmpXchgU32(&pThis->u32State, u32State, u32StateOld))
321 {
322 if (u32State & RTPIPE_POSIX_SWITCHING)
323 break;
324 return VINF_SUCCESS;
325 }
326 }
327 else if (cUsers == 0)
328 {
329 u32State = 1 | RTPIPE_POSIX_SWITCHING | RTPIPE_POSIX_BLOCKING;
330 if (ASMAtomicCmpXchgU32(&pThis->u32State, u32State, u32StateOld))
331 break;
332 }
333 else
334 return VERR_WRONG_ORDER;
335 ASMNopPause();
336 }
337
338 /*
339 * Do the switching.
340 */
341 int fFlags = fcntl(pThis->fd, F_GETFL, 0);
342 if (fFlags != -1)
343 {
344 if ( !(fFlags & O_NONBLOCK)
345 || fcntl(pThis->fd, F_SETFL, fFlags & ~O_NONBLOCK) != -1)
346 {
347 ASMAtomicBitClear(&pThis->u32State, RTPIPE_POSIX_SWITCHING_BIT);
348 return VINF_SUCCESS;
349 }
350 }
351
352 ASMAtomicDecU32(&pThis->u32State);
353 return RTErrConvertFromErrno(errno);
354}
355
356
357/**
358 * Prepare non-blocking mode.
359 *
360 * @returns VINF_SUCCESS
361 * @retval VERR_WRONG_ORDER
362 * @retval VERR_INTERNAL_ERROR_4
363 *
364 * @param pThis The pipe handle.
365 */
366static int rtPipeTryNonBlocking(RTPIPEINTERNAL *pThis)
367{
368 /*
369 * Update the state.
370 */
371 for (;;)
372 {
373 uint32_t u32State = ASMAtomicReadU32(&pThis->u32State);
374 uint32_t const u32StateOld = u32State;
375 uint32_t const cUsers = (u32State & RTPIPE_POSIX_USERS_MASK);
376
377 if (!(u32State & RTPIPE_POSIX_BLOCKING))
378 {
379 AssertReturn(cUsers < RTPIPE_POSIX_USERS_MASK / 2, VERR_INTERNAL_ERROR_4);
380 u32State &= ~RTPIPE_POSIX_USERS_MASK;
381 u32State |= cUsers + 1;
382 if (ASMAtomicCmpXchgU32(&pThis->u32State, u32State, u32StateOld))
383 {
384 if (u32State & RTPIPE_POSIX_SWITCHING)
385 break;
386 return VINF_SUCCESS;
387 }
388 }
389 else if (cUsers == 0)
390 {
391 u32State = 1 | RTPIPE_POSIX_SWITCHING;
392 if (ASMAtomicCmpXchgU32(&pThis->u32State, u32State, u32StateOld))
393 break;
394 }
395 else
396 return VERR_WRONG_ORDER;
397 ASMNopPause();
398 }
399
400 /*
401 * Do the switching.
402 */
403 int fFlags = fcntl(pThis->fd, F_GETFL, 0);
404 if (fFlags != -1)
405 {
406 if ( (fFlags & O_NONBLOCK)
407 || fcntl(pThis->fd, F_SETFL, fFlags | O_NONBLOCK) != -1)
408 {
409 ASMAtomicBitClear(&pThis->u32State, RTPIPE_POSIX_SWITCHING_BIT);
410 return VINF_SUCCESS;
411 }
412 }
413
414 ASMAtomicDecU32(&pThis->u32State);
415 return RTErrConvertFromErrno(errno);
416}
417
418
419/**
420 * Checks if the read pipe has a HUP condition.
421 *
422 * @returns true if HUP, false if no.
423 * @param pThis The pipe handle (read).
424 */
425static bool rtPipePosixHasHup(RTPIPEINTERNAL *pThis)
426{
427 Assert(pThis->fRead);
428
429 struct pollfd PollFd;
430 RT_ZERO(PollFd);
431 PollFd.fd = pThis->fd;
432 PollFd.events = POLLHUP;
433 return poll(&PollFd, 1, 0) >= 1
434 && (PollFd.revents & POLLHUP);
435}
436
437
438RTDECL(int) RTPipeRead(RTPIPE hPipe, void *pvBuf, size_t cbToRead, size_t *pcbRead)
439{
440 RTPIPEINTERNAL *pThis = hPipe;
441 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
442 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
443 AssertReturn(pThis->fRead, VERR_ACCESS_DENIED);
444 AssertPtr(pcbRead);
445 AssertPtr(pvBuf);
446
447 int rc = rtPipeTryNonBlocking(pThis);
448 if (RT_SUCCESS(rc))
449 {
450 ssize_t cbRead = read(pThis->fd, pvBuf, RT_MIN(cbToRead, SSIZE_MAX));
451 if (cbRead >= 0)
452 {
453 if (cbRead || !cbToRead || !rtPipePosixHasHup(pThis))
454 *pcbRead = cbRead;
455 else
456 rc = VERR_BROKEN_PIPE;
457 }
458 else if (errno == EAGAIN)
459 {
460 *pcbRead = 0;
461 rc = VINF_TRY_AGAIN;
462 }
463 else
464 rc = RTErrConvertFromErrno(errno);
465
466 ASMAtomicDecU32(&pThis->u32State);
467 }
468 return rc;
469}
470
471
472RTDECL(int) RTPipeReadBlocking(RTPIPE hPipe, void *pvBuf, size_t cbToRead, size_t *pcbRead)
473{
474 RTPIPEINTERNAL *pThis = hPipe;
475 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
476 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
477 AssertReturn(pThis->fRead, VERR_ACCESS_DENIED);
478 AssertPtr(pvBuf);
479
480 int rc = rtPipeTryBlocking(pThis);
481 if (RT_SUCCESS(rc))
482 {
483 size_t cbTotalRead = 0;
484 while (cbToRead > 0)
485 {
486 ssize_t cbRead = read(pThis->fd, pvBuf, RT_MIN(cbToRead, SSIZE_MAX));
487 if (cbRead < 0)
488 {
489 rc = RTErrConvertFromErrno(errno);
490 break;
491 }
492 if (!cbRead && rtPipePosixHasHup(pThis))
493 {
494 rc = VERR_BROKEN_PIPE;
495 break;
496 }
497
498 /* advance */
499 pvBuf = (char *)pvBuf + cbRead;
500 cbTotalRead += cbRead;
501 cbToRead -= cbRead;
502 }
503
504 if (pcbRead)
505 {
506 *pcbRead = cbTotalRead;
507 if ( RT_FAILURE(rc)
508 && cbTotalRead
509 && rc != VERR_INVALID_POINTER)
510 rc = VINF_SUCCESS;
511 }
512
513 ASMAtomicDecU32(&pThis->u32State);
514 }
515 return rc;
516}
517
518
519RTDECL(int) RTPipeWrite(RTPIPE hPipe, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
520{
521 RTPIPEINTERNAL *pThis = hPipe;
522 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
523 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
524 AssertReturn(!pThis->fRead, VERR_ACCESS_DENIED);
525 AssertPtr(pcbWritten);
526 AssertPtr(pvBuf);
527
528 int rc = rtPipeTryNonBlocking(pThis);
529 if (RT_SUCCESS(rc))
530 {
531 if (cbToWrite)
532 {
533 ssize_t cbWritten = write(pThis->fd, pvBuf, RT_MIN(cbToWrite, SSIZE_MAX));
534 if (cbWritten >= 0)
535 *pcbWritten = cbWritten;
536 else if (errno == EAGAIN)
537 {
538 *pcbWritten = 0;
539 rc = VINF_TRY_AGAIN;
540 }
541 else
542 rc = RTErrConvertFromErrno(errno);
543 }
544 else
545 *pcbWritten = 0;
546
547 ASMAtomicDecU32(&pThis->u32State);
548 }
549 return rc;
550}
551
552
553RTDECL(int) RTPipeWriteBlocking(RTPIPE hPipe, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
554{
555 RTPIPEINTERNAL *pThis = hPipe;
556 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
557 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
558 AssertReturn(!pThis->fRead, VERR_ACCESS_DENIED);
559 AssertPtr(pvBuf);
560 AssertPtrNull(pcbWritten);
561
562 int rc = rtPipeTryBlocking(pThis);
563 if (RT_SUCCESS(rc))
564 {
565 size_t cbTotalWritten = 0;
566 while (cbToWrite > 0)
567 {
568 ssize_t cbWritten = write(pThis->fd, pvBuf, RT_MIN(cbToWrite, SSIZE_MAX));
569 if (cbWritten < 0)
570 {
571 rc = RTErrConvertFromErrno(errno);
572 break;
573 }
574
575 /* advance */
576 pvBuf = (char const *)pvBuf + cbWritten;
577 cbTotalWritten += cbWritten;
578 cbToWrite -= cbWritten;
579 }
580
581 if (pcbWritten)
582 {
583 *pcbWritten = cbTotalWritten;
584 if ( RT_FAILURE(rc)
585 && cbTotalWritten
586 && rc != VERR_INVALID_POINTER)
587 rc = VINF_SUCCESS;
588 }
589
590 ASMAtomicDecU32(&pThis->u32State);
591 }
592 return rc;
593}
594
595
596RTDECL(int) RTPipeFlush(RTPIPE hPipe)
597{
598 RTPIPEINTERNAL *pThis = hPipe;
599 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
600 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
601 AssertReturn(!pThis->fRead, VERR_ACCESS_DENIED);
602
603 if (fsync(pThis->fd))
604 {
605 if (errno == EINVAL || errno == ENOTSUP)
606 return VERR_NOT_SUPPORTED;
607 return RTErrConvertFromErrno(errno);
608 }
609 return VINF_SUCCESS;
610}
611
612
613RTDECL(int) RTPipeSelectOne(RTPIPE hPipe, RTMSINTERVAL cMillies)
614{
615 RTPIPEINTERNAL *pThis = hPipe;
616 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
617 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
618
619 struct pollfd PollFd;
620 RT_ZERO(PollFd);
621 PollFd.fd = pThis->fd;
622 PollFd.events = POLLHUP | POLLERR;
623 if (pThis->fRead)
624 PollFd.events |= POLLIN | POLLPRI;
625 else
626 PollFd.events |= POLLOUT;
627
628 int timeout;
629 if ( cMillies == RT_INDEFINITE_WAIT
630 || cMillies >= INT_MAX /* lazy bird */)
631 timeout = -1;
632 else
633 timeout = cMillies;
634
635 int rc = poll(&PollFd, 1, 0);
636 if (rc == -1)
637 return RTErrConvertFromErrno(errno);
638 return rc > 0 ? VINF_SUCCESS : VERR_TIMEOUT;
639}
640
641
642RTDECL(int) RTPipeQueryReadable(RTPIPE hPipe, size_t *pcbReadable)
643{
644 RTPIPEINTERNAL *pThis = hPipe;
645 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
646 AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
647 AssertReturn(pThis->fRead, VERR_PIPE_NOT_READ);
648 AssertPtrReturn(pcbReadable, VERR_INVALID_POINTER);
649
650 int cb = 0;
651 int rc = ioctl(pThis->fd, FIONREAD, &cb);
652 if (rc != -1)
653 {
654 AssertStmt(cb >= 0, cb = 0);
655 *pcbReadable = cb;
656 return VINF_SUCCESS;
657 }
658
659 rc = errno;
660 if (rc == ENOTTY)
661 rc = VERR_NOT_SUPPORTED;
662 else
663 rc = RTErrConvertFromErrno(rc);
664 return rc;
665}
666
Note: See TracBrowser for help on using the repository browser.

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