VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/posix/localipc-posix.cpp@ 75883

Last change on this file since 75883 was 73097, checked in by vboxsync, 6 years ago

*: Made RT_UOFFSETOF, RT_OFFSETOF, RT_UOFFSETOF_ADD and RT_OFFSETOF_ADD work like builtin_offsetof() and require compile time resolvable requests, adding RT_UOFFSETOF_DYN for the dynamic questions that can only be answered at runtime.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 35.0 KB
Line 
1/* $Id: localipc-posix.cpp 73097 2018-07-12 21:06:33Z vboxsync $ */
2/** @file
3 * IPRT - Local IPC Server & Client, Posix.
4 */
5
6/*
7 * Copyright (C) 2006-2017 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#define LOG_GROUP RTLOGGROUP_LOCALIPC
32#include "internal/iprt.h"
33#include <iprt/localipc.h>
34
35#include <iprt/asm.h>
36#include <iprt/assert.h>
37#include <iprt/ctype.h>
38#include <iprt/critsect.h>
39#include <iprt/mem.h>
40#include <iprt/log.h>
41#include <iprt/poll.h>
42#include <iprt/socket.h>
43#include <iprt/string.h>
44#include <iprt/time.h>
45
46#include <sys/types.h>
47#include <sys/socket.h>
48#include <sys/un.h>
49#ifndef RT_OS_OS2
50# include <sys/poll.h>
51#endif
52#include <errno.h>
53#include <fcntl.h>
54#include <signal.h>
55#include <unistd.h>
56
57#include "internal/magics.h"
58#include "internal/path.h"
59#include "internal/socket.h"
60
61
62/*********************************************************************************************************************************
63* Structures and Typedefs *
64*********************************************************************************************************************************/
65/**
66 * Local IPC service instance, POSIX.
67 */
68typedef struct RTLOCALIPCSERVERINT
69{
70 /** The magic (RTLOCALIPCSERVER_MAGIC). */
71 uint32_t u32Magic;
72 /** The creation flags. */
73 uint32_t fFlags;
74 /** Critical section protecting the structure. */
75 RTCRITSECT CritSect;
76 /** The number of references to the instance. */
77 uint32_t volatile cRefs;
78 /** Indicates that there is a pending cancel request. */
79 bool volatile fCancelled;
80 /** The server socket. */
81 RTSOCKET hSocket;
82 /** Thread currently listening for clients. */
83 RTTHREAD hListenThread;
84 /** The name we bound the server to (native charset encoding). */
85 struct sockaddr_un Name;
86} RTLOCALIPCSERVERINT;
87/** Pointer to a local IPC server instance (POSIX). */
88typedef RTLOCALIPCSERVERINT *PRTLOCALIPCSERVERINT;
89
90
91/**
92 * Local IPC session instance, POSIX.
93 */
94typedef struct RTLOCALIPCSESSIONINT
95{
96 /** The magic (RTLOCALIPCSESSION_MAGIC). */
97 uint32_t u32Magic;
98 /** Critical section protecting the structure. */
99 RTCRITSECT CritSect;
100 /** The number of references to the instance. */
101 uint32_t volatile cRefs;
102 /** Indicates that there is a pending cancel request. */
103 bool volatile fCancelled;
104 /** Set if this is the server side, clear if the client. */
105 bool fServerSide;
106 /** The client socket. */
107 RTSOCKET hSocket;
108 /** Thread currently doing read related activites. */
109 RTTHREAD hWriteThread;
110 /** Thread currently doing write related activies. */
111 RTTHREAD hReadThread;
112} RTLOCALIPCSESSIONINT;
113/** Pointer to a local IPC session instance (Windows). */
114typedef RTLOCALIPCSESSIONINT *PRTLOCALIPCSESSIONINT;
115
116
117/** Local IPC name prefix for portable names. */
118#define RTLOCALIPC_POSIX_NAME_PREFIX "/tmp/.iprt-localipc-"
119
120
121/**
122 * Validates the user specified name.
123 *
124 * @returns IPRT status code.
125 * @param pszName The name to validate.
126 * @param fNative Whether it's a native name or a portable name.
127 */
128static int rtLocalIpcPosixValidateName(const char *pszName, bool fNative)
129{
130 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
131 AssertReturn(*pszName, VERR_INVALID_NAME);
132
133 if (!fNative)
134 {
135 for (;;)
136 {
137 char ch = *pszName++;
138 if (!ch)
139 break;
140 AssertReturn(!RT_C_IS_CNTRL(ch), VERR_INVALID_NAME);
141 AssertReturn((unsigned)ch < 0x80, VERR_INVALID_NAME);
142 AssertReturn(ch != '\\', VERR_INVALID_NAME);
143 AssertReturn(ch != '/', VERR_INVALID_NAME);
144 }
145 }
146 else
147 {
148 int rc = RTStrValidateEncoding(pszName);
149 AssertRCReturn(rc, rc);
150 }
151
152 return VINF_SUCCESS;
153}
154
155
156/**
157 * Constructs a local (unix) domain socket name.
158 *
159 * @returns IPRT status code.
160 * @param pAddr The address structure to construct the name in.
161 * @param pcbAddr Where to return the address size.
162 * @param pszName The user specified name (valid).
163 * @param fNative Whether it's a native name or a portable name.
164 */
165static int rtLocalIpcPosixConstructName(struct sockaddr_un *pAddr, uint8_t *pcbAddr, const char *pszName, bool fNative)
166{
167 const char *pszNativeName;
168 int rc = rtPathToNative(&pszNativeName, pszName, NULL /*pszBasePath not support*/);
169 if (RT_SUCCESS(rc))
170 {
171 size_t cchNativeName = strlen(pszNativeName);
172 size_t cbFull = !fNative ? cchNativeName + sizeof(RTLOCALIPC_POSIX_NAME_PREFIX) : cchNativeName + 1;
173 if (cbFull <= sizeof(pAddr->sun_path))
174 {
175 RT_ZERO(*pAddr);
176#ifdef RT_OS_OS2 /* Size must be exactly right on OS/2. */
177 *pcbAddr = sizeof(*pAddr);
178#else
179 *pcbAddr = RT_UOFFSETOF(struct sockaddr_un, sun_path) + (uint8_t)cbFull;
180#endif
181#ifdef HAVE_SUN_LEN_MEMBER
182 pAddr->sun_len = *pcbAddr;
183#endif
184 pAddr->sun_family = AF_LOCAL;
185
186 if (!fNative)
187 {
188 memcpy(pAddr->sun_path, RTLOCALIPC_POSIX_NAME_PREFIX, sizeof(RTLOCALIPC_POSIX_NAME_PREFIX) - 1);
189 memcpy(&pAddr->sun_path[sizeof(RTLOCALIPC_POSIX_NAME_PREFIX) - 1], pszNativeName, cchNativeName + 1);
190 }
191 else
192 memcpy(pAddr->sun_path, pszNativeName, cchNativeName + 1);
193 }
194 else
195 rc = VERR_FILENAME_TOO_LONG;
196 rtPathFreeNative(pszNativeName, pszName);
197 }
198 return rc;
199}
200
201
202
203RTDECL(int) RTLocalIpcServerCreate(PRTLOCALIPCSERVER phServer, const char *pszName, uint32_t fFlags)
204{
205 /*
206 * Parameter validation.
207 */
208 AssertPtrReturn(phServer, VERR_INVALID_POINTER);
209 *phServer = NIL_RTLOCALIPCSERVER;
210 AssertReturn(!(fFlags & ~RTLOCALIPC_FLAGS_VALID_MASK), VERR_INVALID_FLAGS);
211 int rc = rtLocalIpcPosixValidateName(pszName, RT_BOOL(fFlags & RTLOCALIPC_FLAGS_NATIVE_NAME));
212 if (RT_SUCCESS(rc))
213 {
214 /*
215 * Allocate memory for the instance and initialize it.
216 */
217 PRTLOCALIPCSERVERINT pThis = (PRTLOCALIPCSERVERINT)RTMemAllocZ(sizeof(*pThis));
218 if (pThis)
219 {
220 pThis->u32Magic = RTLOCALIPCSERVER_MAGIC;
221 pThis->fFlags = fFlags;
222 pThis->cRefs = 1;
223 pThis->fCancelled = false;
224 pThis->hListenThread = NIL_RTTHREAD;
225 rc = RTCritSectInit(&pThis->CritSect);
226 if (RT_SUCCESS(rc))
227 {
228 /*
229 * Create the local (unix) socket and bind to it.
230 */
231 rc = rtSocketCreate(&pThis->hSocket, AF_LOCAL, SOCK_STREAM, 0 /*iProtocol*/);
232 if (RT_SUCCESS(rc))
233 {
234 RTSocketSetInheritance(pThis->hSocket, false /*fInheritable*/);
235 signal(SIGPIPE, SIG_IGN); /* Required on solaris, at least. */
236
237 uint8_t cbAddr;
238 rc = rtLocalIpcPosixConstructName(&pThis->Name, &cbAddr, pszName,
239 RT_BOOL(fFlags & RTLOCALIPC_FLAGS_NATIVE_NAME));
240 if (RT_SUCCESS(rc))
241 {
242 rc = rtSocketBindRawAddr(pThis->hSocket, &pThis->Name, cbAddr);
243 if (rc == VERR_NET_ADDRESS_IN_USE)
244 {
245 unlink(pThis->Name.sun_path);
246 rc = rtSocketBindRawAddr(pThis->hSocket, &pThis->Name, cbAddr);
247 }
248 if (RT_SUCCESS(rc))
249 {
250 rc = rtSocketListen(pThis->hSocket, 16);
251 if (RT_SUCCESS(rc))
252 {
253 LogFlow(("RTLocalIpcServerCreate: Created %p (%s)\n", pThis, pThis->Name.sun_path));
254 *phServer = pThis;
255 return VINF_SUCCESS;
256 }
257 unlink(pThis->Name.sun_path);
258 }
259 }
260 RTSocketRelease(pThis->hSocket);
261 }
262 RTCritSectDelete(&pThis->CritSect);
263 }
264 RTMemFree(pThis);
265 }
266 else
267 rc = VERR_NO_MEMORY;
268 }
269 Log(("RTLocalIpcServerCreate: failed, rc=%Rrc\n", rc));
270 return rc;
271}
272
273
274/**
275 * Retains a reference to the server instance.
276 *
277 * @returns
278 * @param pThis The server instance.
279 */
280DECLINLINE(void) rtLocalIpcServerRetain(PRTLOCALIPCSERVERINT pThis)
281{
282 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
283 Assert(cRefs < UINT32_MAX / 2 && cRefs); RT_NOREF_PV(cRefs);
284}
285
286
287/**
288 * Server instance destructor.
289 *
290 * @returns VINF_OBJECT_DESTROYED
291 * @param pThis The server instance.
292 */
293static int rtLocalIpcServerDtor(PRTLOCALIPCSERVERINT pThis)
294{
295 pThis->u32Magic = ~RTLOCALIPCSERVER_MAGIC;
296 if (RTSocketRelease(pThis->hSocket) == 0)
297 Log(("rtLocalIpcServerDtor: Released socket\n"));
298 else
299 Log(("rtLocalIpcServerDtor: Socket still has references (impossible?)\n"));
300 RTCritSectDelete(&pThis->CritSect);
301 unlink(pThis->Name.sun_path);
302 RTMemFree(pThis);
303 return VINF_OBJECT_DESTROYED;
304}
305
306
307/**
308 * Releases a reference to the server instance.
309 *
310 * @returns VINF_SUCCESS if only release, VINF_OBJECT_DESTROYED if destroyed.
311 * @param pThis The server instance.
312 */
313DECLINLINE(int) rtLocalIpcServerRelease(PRTLOCALIPCSERVERINT pThis)
314{
315 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
316 Assert(cRefs < UINT32_MAX / 2);
317 if (!cRefs)
318 return rtLocalIpcServerDtor(pThis);
319 return VINF_SUCCESS;
320}
321
322
323/**
324 * The core of RTLocalIpcServerCancel, used by both the destroy and cancel APIs.
325 *
326 * @returns IPRT status code
327 * @param pThis The server instance.
328 */
329static int rtLocalIpcServerCancel(PRTLOCALIPCSERVERINT pThis)
330{
331 RTCritSectEnter(&pThis->CritSect);
332 pThis->fCancelled = true;
333 Log(("rtLocalIpcServerCancel:\n"));
334 if (pThis->hListenThread != NIL_RTTHREAD)
335 RTThreadPoke(pThis->hListenThread);
336 RTCritSectLeave(&pThis->CritSect);
337 return VINF_SUCCESS;
338}
339
340
341
342RTDECL(int) RTLocalIpcServerDestroy(RTLOCALIPCSERVER hServer)
343{
344 /*
345 * Validate input.
346 */
347 if (hServer == NIL_RTLOCALIPCSERVER)
348 return VINF_SUCCESS;
349 PRTLOCALIPCSERVERINT pThis = (PRTLOCALIPCSERVERINT)hServer;
350 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
351 AssertReturn(pThis->u32Magic == RTLOCALIPCSERVER_MAGIC, VERR_INVALID_HANDLE);
352
353 /*
354 * Invalidate the server, releasing the caller's reference to the instance
355 * data and making sure any other thread in the listen API will wake up.
356 */
357 AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, ~RTLOCALIPCSERVER_MAGIC, RTLOCALIPCSERVER_MAGIC), VERR_WRONG_ORDER);
358
359 rtLocalIpcServerCancel(pThis);
360 return rtLocalIpcServerRelease(pThis);
361}
362
363
364RTDECL(int) RTLocalIpcServerCancel(RTLOCALIPCSERVER hServer)
365{
366 /*
367 * Validate input.
368 */
369 PRTLOCALIPCSERVERINT pThis = (PRTLOCALIPCSERVERINT)hServer;
370 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
371 AssertReturn(pThis->u32Magic == RTLOCALIPCSERVER_MAGIC, VERR_INVALID_HANDLE);
372
373 /*
374 * Do the job.
375 */
376 rtLocalIpcServerRetain(pThis);
377 rtLocalIpcServerCancel(pThis);
378 rtLocalIpcServerRelease(pThis);
379 return VINF_SUCCESS;
380}
381
382
383RTDECL(int) RTLocalIpcServerListen(RTLOCALIPCSERVER hServer, PRTLOCALIPCSESSION phClientSession)
384{
385 /*
386 * Validate input.
387 */
388 PRTLOCALIPCSERVERINT pThis = (PRTLOCALIPCSERVERINT)hServer;
389 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
390 AssertReturn(pThis->u32Magic == RTLOCALIPCSERVER_MAGIC, VERR_INVALID_HANDLE);
391
392 /*
393 * Begin listening.
394 */
395 rtLocalIpcServerRetain(pThis);
396 int rc = RTCritSectEnter(&pThis->CritSect);
397 if (RT_SUCCESS(rc))
398 {
399 if (pThis->hListenThread == NIL_RTTHREAD)
400 {
401 pThis->hListenThread = RTThreadSelf();
402
403 /*
404 * The listening retry loop.
405 */
406 for (;;)
407 {
408 if (!pThis->fCancelled)
409 {
410 rc = RTCritSectLeave(&pThis->CritSect);
411 AssertRCBreak(rc);
412
413 struct sockaddr_un Addr;
414 size_t cbAddr = sizeof(Addr);
415 RTSOCKET hClient;
416 Log(("RTLocalIpcServerListen: Calling rtSocketAccept...\n"));
417 rc = rtSocketAccept(pThis->hSocket, &hClient, (struct sockaddr *)&Addr, &cbAddr);
418 Log(("RTLocalIpcServerListen: rtSocketAccept returns %Rrc.\n", rc));
419
420 int rc2 = RTCritSectEnter(&pThis->CritSect);
421 AssertRCBreakStmt(rc2, rc = RT_SUCCESS(rc) ? rc2 : rc);
422
423 if (RT_SUCCESS(rc))
424 {
425 /*
426 * Create a client session.
427 */
428 PRTLOCALIPCSESSIONINT pSession = (PRTLOCALIPCSESSIONINT)RTMemAllocZ(sizeof(*pSession));
429 if (pSession)
430 {
431 pSession->u32Magic = RTLOCALIPCSESSION_MAGIC;
432 pSession->cRefs = 1;
433 pSession->fCancelled = false;
434 pSession->fServerSide = true;
435 pSession->hSocket = hClient;
436 pSession->hReadThread = NIL_RTTHREAD;
437 pSession->hWriteThread = NIL_RTTHREAD;
438 rc = RTCritSectInit(&pSession->CritSect);
439 if (RT_SUCCESS(rc))
440 {
441 Log(("RTLocalIpcServerListen: Returning new client session: %p\n", pSession));
442 *phClientSession = pSession;
443 break;
444 }
445
446 RTMemFree(pSession);
447 }
448 else
449 rc = VERR_NO_MEMORY;
450 }
451 else if ( rc != VERR_INTERRUPTED
452 && rc != VERR_TRY_AGAIN)
453 break;
454 }
455 else
456 {
457 rc = VERR_CANCELLED;
458 break;
459 }
460 }
461
462 pThis->hListenThread = NIL_RTTHREAD;
463 }
464 else
465 {
466 AssertFailed();
467 rc = VERR_RESOURCE_BUSY;
468 }
469 int rc2 = RTCritSectLeave(&pThis->CritSect);
470 AssertStmt(RT_SUCCESS(rc2), rc = RT_SUCCESS(rc) ? rc2 : rc);
471 }
472 rtLocalIpcServerRelease(pThis);
473
474 Log(("RTLocalIpcServerListen: returns %Rrc\n", rc));
475 return rc;
476}
477
478
479RTDECL(int) RTLocalIpcSessionConnect(PRTLOCALIPCSESSION phSession, const char *pszName, uint32_t fFlags)
480{
481 /*
482 * Parameter validation.
483 */
484 AssertPtrReturn(phSession, VERR_INVALID_POINTER);
485 *phSession = NIL_RTLOCALIPCSESSION;
486
487 AssertReturn(!(fFlags & ~RTLOCALIPC_C_FLAGS_VALID_MASK), VERR_INVALID_FLAGS);
488
489 int rc = rtLocalIpcPosixValidateName(pszName, RT_BOOL(fFlags & RTLOCALIPC_C_FLAGS_NATIVE_NAME));
490 if (RT_SUCCESS(rc))
491 {
492 /*
493 * Allocate memory for the instance and initialize it.
494 */
495 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)RTMemAllocZ(sizeof(*pThis));
496 if (pThis)
497 {
498 pThis->u32Magic = RTLOCALIPCSESSION_MAGIC;
499 pThis->cRefs = 1;
500 pThis->fCancelled = false;
501 pThis->fServerSide = false;
502 pThis->hSocket = NIL_RTSOCKET;
503 pThis->hReadThread = NIL_RTTHREAD;
504 pThis->hWriteThread = NIL_RTTHREAD;
505 rc = RTCritSectInit(&pThis->CritSect);
506 if (RT_SUCCESS(rc))
507 {
508 /*
509 * Create the local (unix) socket and try connect to the server.
510 */
511 rc = rtSocketCreate(&pThis->hSocket, AF_LOCAL, SOCK_STREAM, 0 /*iProtocol*/);
512 if (RT_SUCCESS(rc))
513 {
514 RTSocketSetInheritance(pThis->hSocket, false /*fInheritable*/);
515 signal(SIGPIPE, SIG_IGN); /* Required on solaris, at least. */
516
517 struct sockaddr_un Addr;
518 uint8_t cbAddr;
519 rc = rtLocalIpcPosixConstructName(&Addr, &cbAddr, pszName, RT_BOOL(fFlags & RTLOCALIPC_C_FLAGS_NATIVE_NAME));
520 if (RT_SUCCESS(rc))
521 {
522 rc = rtSocketConnectRaw(pThis->hSocket, &Addr, cbAddr);
523 if (RT_SUCCESS(rc))
524 {
525 *phSession = pThis;
526 Log(("RTLocalIpcSessionConnect: Returns new session %p\n", pThis));
527 return VINF_SUCCESS;
528 }
529 }
530 RTCritSectDelete(&pThis->CritSect);
531 }
532 }
533 RTMemFree(pThis);
534 }
535 else
536 rc = VERR_NO_MEMORY;
537 }
538 Log(("RTLocalIpcSessionConnect: returns %Rrc\n", rc));
539 return rc;
540}
541
542
543/**
544 * Retains a reference to the session instance.
545 *
546 * @param pThis The server instance.
547 */
548DECLINLINE(void) rtLocalIpcSessionRetain(PRTLOCALIPCSESSIONINT pThis)
549{
550 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
551 Assert(cRefs < UINT32_MAX / 2 && cRefs); RT_NOREF_PV(cRefs);
552}
553
554
555RTDECL(uint32_t) RTLocalIpcSessionRetain(RTLOCALIPCSESSION hSession)
556{
557 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
558 AssertPtrReturn(pThis, UINT32_MAX);
559 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, UINT32_MAX);
560
561 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
562 Assert(cRefs < UINT32_MAX / 2 && cRefs);
563 return cRefs;
564}
565
566
567/**
568 * Session instance destructor.
569 *
570 * @returns VINF_OBJECT_DESTROYED
571 * @param pThis The server instance.
572 */
573static int rtLocalIpcSessionDtor(PRTLOCALIPCSESSIONINT pThis)
574{
575 pThis->u32Magic = ~RTLOCALIPCSESSION_MAGIC;
576 if (RTSocketRelease(pThis->hSocket) == 0)
577 Log(("rtLocalIpcSessionDtor: Released socket\n"));
578 else
579 Log(("rtLocalIpcSessionDtor: Socket still has references (impossible?)\n"));
580 RTCritSectDelete(&pThis->CritSect);
581 RTMemFree(pThis);
582 return VINF_OBJECT_DESTROYED;
583}
584
585
586/**
587 * Releases a reference to the session instance.
588 *
589 * @returns VINF_SUCCESS or VINF_OBJECT_DESTROYED as appropriate.
590 * @param pThis The session instance.
591 */
592DECLINLINE(int) rtLocalIpcSessionRelease(PRTLOCALIPCSESSIONINT pThis)
593{
594 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
595 Assert(cRefs < UINT32_MAX / 2);
596 if (!cRefs)
597 return rtLocalIpcSessionDtor(pThis);
598 Log(("rtLocalIpcSessionRelease: %u refs left\n", cRefs));
599 return VINF_SUCCESS;
600}
601
602
603RTDECL(uint32_t) RTLocalIpcSessionRelease(RTLOCALIPCSESSION hSession)
604{
605 if (hSession == NIL_RTLOCALIPCSESSION)
606 return 0;
607
608 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
609 AssertPtrReturn(pThis, UINT32_MAX);
610 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, UINT32_MAX);
611
612 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
613 Assert(cRefs < UINT32_MAX / 2);
614 if (cRefs)
615 Log(("RTLocalIpcSessionRelease: %u refs left\n", cRefs));
616 else
617 rtLocalIpcSessionDtor(pThis);
618 return cRefs;
619}
620
621
622/**
623 * The core of RTLocalIpcSessionCancel, used by both the destroy and cancel APIs.
624 *
625 * @returns IPRT status code
626 * @param pThis The session instance.
627 */
628static int rtLocalIpcSessionCancel(PRTLOCALIPCSESSIONINT pThis)
629{
630 RTCritSectEnter(&pThis->CritSect);
631 pThis->fCancelled = true;
632 Log(("rtLocalIpcSessionCancel:\n"));
633 if (pThis->hReadThread != NIL_RTTHREAD)
634 RTThreadPoke(pThis->hReadThread);
635 if (pThis->hWriteThread != NIL_RTTHREAD)
636 RTThreadPoke(pThis->hWriteThread);
637 RTCritSectLeave(&pThis->CritSect);
638 return VINF_SUCCESS;
639}
640
641
642RTDECL(int) RTLocalIpcSessionClose(RTLOCALIPCSESSION hSession)
643{
644 /*
645 * Validate input.
646 */
647 if (hSession == NIL_RTLOCALIPCSESSION)
648 return VINF_SUCCESS;
649 PRTLOCALIPCSESSIONINT pThis = hSession;
650 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
651 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
652
653 /*
654 * Invalidate the session, releasing the caller's reference to the instance
655 * data and making sure any other thread in the listen API will wake up.
656 */
657 Log(("RTLocalIpcSessionClose:\n"));
658
659 rtLocalIpcSessionCancel(pThis);
660 return rtLocalIpcSessionRelease(pThis);
661}
662
663
664RTDECL(int) RTLocalIpcSessionCancel(RTLOCALIPCSESSION hSession)
665{
666 /*
667 * Validate input.
668 */
669 PRTLOCALIPCSESSIONINT pThis = hSession;
670 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
671 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
672
673 /*
674 * Do the job.
675 */
676 rtLocalIpcSessionRetain(pThis);
677 rtLocalIpcSessionCancel(pThis);
678 rtLocalIpcSessionRelease(pThis);
679 return VINF_SUCCESS;
680}
681
682
683/**
684 * Checks if the socket has has a HUP condition after reading zero bytes.
685 *
686 * @returns true if HUP, false if no.
687 * @param pThis The IPC session handle.
688 */
689static bool rtLocalIpcPosixHasHup(PRTLOCALIPCSESSIONINT pThis)
690{
691 int fdNative = RTSocketToNative(pThis->hSocket);
692
693#if !defined(RT_OS_OS2) && !defined(RT_OS_SOLARIS)
694 struct pollfd PollFd;
695 RT_ZERO(PollFd);
696 PollFd.fd = fdNative;
697 PollFd.events = POLLHUP | POLLERR;
698 if (poll(&PollFd, 1, 0) <= 0)
699 return false;
700 if (!(PollFd.revents & (POLLHUP | POLLERR)))
701 return false;
702#else /* RT_OS_OS2 || RT_OS_SOLARIS */
703 /*
704 * OS/2: No native poll, do zero byte send to check for EPIPE.
705 * Solaris: We don't get POLLHUP.
706 */
707 uint8_t bDummy;
708 ssize_t rcSend = send(fdNative, &bDummy, 0, 0);
709 if (rcSend >= 0 || (errno != EPIPE && errno != ECONNRESET))
710 return false;
711#endif /* RT_OS_OS2 || RT_OS_SOLARIS */
712
713 /*
714 * We've established EPIPE. Now make sure there aren't any last bytes to
715 * read that came in between the recv made by the caller and the disconnect.
716 */
717 uint8_t bPeek;
718 ssize_t rcRecv = recv(fdNative, &bPeek, 1, MSG_DONTWAIT | MSG_PEEK);
719 return rcRecv <= 0;
720}
721
722
723RTDECL(int) RTLocalIpcSessionRead(RTLOCALIPCSESSION hSession, void *pvBuf, size_t cbToRead, size_t *pcbRead)
724{
725 /*
726 * Validate input.
727 */
728 PRTLOCALIPCSESSIONINT pThis = hSession;
729 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
730 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
731
732 /*
733 * Do the job.
734 */
735 rtLocalIpcSessionRetain(pThis);
736
737 int rc = RTCritSectEnter(&pThis->CritSect);
738 if (RT_SUCCESS(rc))
739 {
740 if (pThis->hReadThread == NIL_RTTHREAD)
741 {
742 pThis->hReadThread = RTThreadSelf();
743
744 for (;;)
745 {
746 if (!pThis->fCancelled)
747 {
748 rc = RTCritSectLeave(&pThis->CritSect);
749 AssertRCBreak(rc);
750
751 rc = RTSocketRead(pThis->hSocket, pvBuf, cbToRead, pcbRead);
752
753 /* Detect broken pipe. */
754 if (rc == VINF_SUCCESS)
755 {
756 if (!pcbRead || *pcbRead)
757 { /* likely */ }
758 else if (rtLocalIpcPosixHasHup(pThis))
759 rc = VERR_BROKEN_PIPE;
760 }
761 else if (rc == VERR_NET_CONNECTION_RESET_BY_PEER || rc == VERR_NET_SHUTDOWN)
762 rc = VERR_BROKEN_PIPE;
763
764 int rc2 = RTCritSectEnter(&pThis->CritSect);
765 AssertRCBreakStmt(rc2, rc = RT_SUCCESS(rc) ? rc2 : rc);
766
767 if ( rc == VERR_INTERRUPTED
768 || rc == VERR_TRY_AGAIN)
769 continue;
770 }
771 else
772 rc = VERR_CANCELLED;
773 break;
774 }
775
776 pThis->hReadThread = NIL_RTTHREAD;
777 }
778 int rc2 = RTCritSectLeave(&pThis->CritSect);
779 AssertStmt(RT_SUCCESS(rc2), rc = RT_SUCCESS(rc) ? rc2 : rc);
780 }
781
782 rtLocalIpcSessionRelease(pThis);
783 return rc;
784}
785
786
787RTDECL(int) RTLocalIpcSessionReadNB(RTLOCALIPCSESSION hSession, void *pvBuf, size_t cbToRead, size_t *pcbRead)
788{
789 /*
790 * Validate input.
791 */
792 PRTLOCALIPCSESSIONINT pThis = hSession;
793 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
794 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
795
796 /*
797 * Do the job.
798 */
799 rtLocalIpcSessionRetain(pThis);
800
801 int rc = RTCritSectEnter(&pThis->CritSect);
802 if (RT_SUCCESS(rc))
803 {
804 if (pThis->hReadThread == NIL_RTTHREAD)
805 {
806 pThis->hReadThread = RTThreadSelf(); /* not really required, but whatever. */
807
808 for (;;)
809 {
810 if (!pThis->fCancelled)
811 {
812 rc = RTSocketReadNB(pThis->hSocket, pvBuf, cbToRead, pcbRead);
813
814 /* Detect broken pipe. */
815 if (rc == VINF_SUCCESS)
816 {
817 if (!pcbRead || *pcbRead)
818 { /* likely */ }
819 else if (rtLocalIpcPosixHasHup(pThis))
820 rc = VERR_BROKEN_PIPE;
821 }
822 else if (rc == VERR_NET_CONNECTION_RESET_BY_PEER || rc == VERR_NET_SHUTDOWN)
823 rc = VERR_BROKEN_PIPE;
824
825 if (rc == VERR_INTERRUPTED)
826 continue;
827 }
828 else
829 rc = VERR_CANCELLED;
830 break;
831 }
832
833 pThis->hReadThread = NIL_RTTHREAD;
834 }
835 int rc2 = RTCritSectLeave(&pThis->CritSect);
836 AssertStmt(RT_SUCCESS(rc2), rc = RT_SUCCESS(rc) ? rc2 : rc);
837 }
838
839 rtLocalIpcSessionRelease(pThis);
840 return rc;
841}
842
843
844RTDECL(int) RTLocalIpcSessionWrite(RTLOCALIPCSESSION hSession, const void *pvBuf, size_t cbToWrite)
845{
846 /*
847 * Validate input.
848 */
849 PRTLOCALIPCSESSIONINT pThis = hSession;
850 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
851 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
852
853 /*
854 * Do the job.
855 */
856 rtLocalIpcSessionRetain(pThis);
857
858 int rc = RTCritSectEnter(&pThis->CritSect);
859 if (RT_SUCCESS(rc))
860 {
861 if (pThis->hWriteThread == NIL_RTTHREAD)
862 {
863 pThis->hWriteThread = RTThreadSelf();
864
865 for (;;)
866 {
867 if (!pThis->fCancelled)
868 {
869 rc = RTCritSectLeave(&pThis->CritSect);
870 AssertRCBreak(rc);
871
872 rc = RTSocketWrite(pThis->hSocket, pvBuf, cbToWrite);
873
874 int rc2 = RTCritSectEnter(&pThis->CritSect);
875 AssertRCBreakStmt(rc2, rc = RT_SUCCESS(rc) ? rc2 : rc);
876
877 if ( rc == VERR_INTERRUPTED
878 || rc == VERR_TRY_AGAIN)
879 continue;
880 }
881 else
882 rc = VERR_CANCELLED;
883 break;
884 }
885
886 pThis->hWriteThread = NIL_RTTHREAD;
887 }
888 int rc2 = RTCritSectLeave(&pThis->CritSect);
889 AssertStmt(RT_SUCCESS(rc2), rc = RT_SUCCESS(rc) ? rc2 : rc);
890 }
891
892 rtLocalIpcSessionRelease(pThis);
893 return rc;
894}
895
896
897RTDECL(int) RTLocalIpcSessionFlush(RTLOCALIPCSESSION hSession)
898{
899 /*
900 * Validate input.
901 */
902 PRTLOCALIPCSESSIONINT pThis = hSession;
903 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
904 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
905
906 /*
907 * This is a no-op because apparently write doesn't return until the
908 * result is read. At least that's what the reply to a 2003-04-08 LKML
909 * posting title "fsync() on unix domain sockets?" indicates.
910 *
911 * For conformity, make sure there isn't any active writes concurrent to this call.
912 */
913 rtLocalIpcSessionRetain(pThis);
914
915 int rc = RTCritSectEnter(&pThis->CritSect);
916 if (RT_SUCCESS(rc))
917 {
918 if (pThis->hWriteThread == NIL_RTTHREAD)
919 rc = RTCritSectLeave(&pThis->CritSect);
920 else
921 {
922 rc = RTCritSectLeave(&pThis->CritSect);
923 if (RT_SUCCESS(rc))
924 rc = VERR_RESOURCE_BUSY;
925 }
926 }
927
928 rtLocalIpcSessionRelease(pThis);
929 return rc;
930}
931
932
933RTDECL(int) RTLocalIpcSessionWaitForData(RTLOCALIPCSESSION hSession, uint32_t cMillies)
934{
935 /*
936 * Validate input.
937 */
938 PRTLOCALIPCSESSIONINT pThis = hSession;
939 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
940 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
941
942 /*
943 * Do the job.
944 */
945 rtLocalIpcSessionRetain(pThis);
946
947 int rc = RTCritSectEnter(&pThis->CritSect);
948 if (RT_SUCCESS(rc))
949 {
950 if (pThis->hReadThread == NIL_RTTHREAD)
951 {
952 pThis->hReadThread = RTThreadSelf();
953 uint64_t const msStart = RTTimeMilliTS();
954 RTMSINTERVAL const cMsOriginalTimeout = cMillies;
955
956 for (;;)
957 {
958 if (!pThis->fCancelled)
959 {
960 rc = RTCritSectLeave(&pThis->CritSect);
961 AssertRCBreak(rc);
962
963 uint32_t fEvents = 0;
964#ifdef RT_OS_OS2
965 /* This doesn't give us any error condition on hangup, so use HUP check. */
966 Log(("RTLocalIpcSessionWaitForData: Calling RTSocketSelectOneEx...\n"));
967 rc = RTSocketSelectOneEx(pThis->hSocket, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, &fEvents, cMillies);
968 Log(("RTLocalIpcSessionWaitForData: RTSocketSelectOneEx returns %Rrc, fEvents=%#x\n", rc, fEvents));
969 if (RT_SUCCESS(rc) && fEvents == RTPOLL_EVT_READ && rtLocalIpcPosixHasHup(pThis))
970 rc = VERR_BROKEN_PIPE;
971#else
972/** @todo RTSocketPoll? */
973 /* POLLHUP will be set on hangup. */
974 struct pollfd PollFd;
975 RT_ZERO(PollFd);
976 PollFd.fd = RTSocketToNative(pThis->hSocket);
977 PollFd.events = POLLHUP | POLLERR | POLLIN;
978 Log(("RTLocalIpcSessionWaitForData: Calling poll...\n"));
979 int cFds = poll(&PollFd, 1, cMillies == RT_INDEFINITE_WAIT ? -1 : cMillies);
980 if (cFds >= 1)
981 {
982 /* Linux & Darwin sets both POLLIN and POLLHUP when the pipe is
983 broken and but no more data to read. Google hints at NetBSD
984 returning more sane values (POLLIN till no more data, then
985 POLLHUP). Solairs OTOH, doesn't ever seem to return POLLHUP. */
986 fEvents = RTPOLL_EVT_READ;
987 if ( (PollFd.revents & (POLLHUP | POLLERR))
988 && !(PollFd.revents & POLLIN))
989 fEvents = RTPOLL_EVT_ERROR;
990# if defined(RT_OS_SOLARIS)
991 else if (PollFd.revents & POLLIN)
992# else
993 else if ((PollFd.revents & (POLLIN | POLLHUP)) == (POLLIN | POLLHUP))
994# endif
995 {
996 /* Check if there is actually data available. */
997 uint8_t bPeek;
998 ssize_t rcRecv = recv(PollFd.fd, &bPeek, 1, MSG_DONTWAIT | MSG_PEEK);
999 if (rcRecv <= 0)
1000 fEvents = RTPOLL_EVT_ERROR;
1001 }
1002 rc = VINF_SUCCESS;
1003 }
1004 else if (rc == 0)
1005 rc = VERR_TIMEOUT;
1006 else
1007 rc = RTErrConvertFromErrno(errno);
1008 Log(("RTLocalIpcSessionWaitForData: poll returns %u (rc=%d), revents=%#x\n", cFds, rc, PollFd.revents));
1009#endif
1010
1011 int rc2 = RTCritSectEnter(&pThis->CritSect);
1012 AssertRCBreakStmt(rc2, rc = RT_SUCCESS(rc) ? rc2 : rc);
1013
1014 if (RT_SUCCESS(rc))
1015 {
1016 if (pThis->fCancelled)
1017 rc = VERR_CANCELLED;
1018 else if (fEvents & RTPOLL_EVT_ERROR)
1019 rc = VERR_BROKEN_PIPE;
1020 }
1021 else if ( rc == VERR_INTERRUPTED
1022 || rc == VERR_TRY_AGAIN)
1023 {
1024 /* Recalc cMillies. */
1025 if (cMsOriginalTimeout != RT_INDEFINITE_WAIT)
1026 {
1027 uint64_t cMsElapsed = RTTimeMilliTS() - msStart;
1028 cMillies = cMsElapsed >= cMsOriginalTimeout ? 0 : cMsOriginalTimeout - (RTMSINTERVAL)cMsElapsed;
1029 }
1030 continue;
1031 }
1032 }
1033 else
1034 rc = VERR_CANCELLED;
1035 break;
1036 }
1037
1038 pThis->hReadThread = NIL_RTTHREAD;
1039 }
1040 int rc2 = RTCritSectLeave(&pThis->CritSect);
1041 AssertStmt(RT_SUCCESS(rc2), rc = RT_SUCCESS(rc) ? rc2 : rc);
1042 }
1043
1044 rtLocalIpcSessionRelease(pThis);
1045 return rc;
1046}
1047
1048
1049RTDECL(int) RTLocalIpcSessionQueryProcess(RTLOCALIPCSESSION hSession, PRTPROCESS pProcess)
1050{
1051 RT_NOREF_PV(hSession); RT_NOREF_PV(pProcess);
1052 return VERR_NOT_SUPPORTED;
1053}
1054
1055
1056RTDECL(int) RTLocalIpcSessionQueryUserId(RTLOCALIPCSESSION hSession, PRTUID pUid)
1057{
1058 RT_NOREF_PV(hSession); RT_NOREF_PV(pUid);
1059 return VERR_NOT_SUPPORTED;
1060}
1061
1062
1063RTDECL(int) RTLocalIpcSessionQueryGroupId(RTLOCALIPCSESSION hSession, PRTGID pGid)
1064{
1065 RT_NOREF_PV(hSession); RT_NOREF_PV(pGid);
1066 return VERR_NOT_SUPPORTED;
1067}
1068
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