VirtualBox

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

Last change on this file since 93025 was 93025, checked in by vboxsync, 3 years ago

IPRT/LocalIpc/poisx: How difficult can this be to get right? bugref:10134

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