VirtualBox

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

Last change on this file since 107965 was 107782, checked in by vboxsync, 4 months ago

Runtime/r3/posix/localipc-posix.cpp: Missing break in out of memory condition, bugref:3409

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