VirtualBox

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

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

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 37.9 KB
Line 
1/* $Id: localipc-posix.cpp 93115 2022-01-01 11:31:46Z vboxsync $ */
2/** @file
3 * IPRT - Local IPC Server & Client, Posix.
4 */
5
6/*
7 * Copyright (C) 2006-2022 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 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
280 AssertReturn(pThis->u32Magic == RTLOCALIPCSERVER_MAGIC, VERR_INVALID_HANDLE);
281 AssertReturn(pThis->Name.sun_path[0] != '\0', VERR_INVALID_STATE);
282
283 if (chown(pThis->Name.sun_path, -1, gid) == 0)
284 {
285 if (chmod(pThis->Name.sun_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) == 0)
286 {
287 LogRel2(("RTLocalIpcServerGrantGroupAccess: IPC socket %s access has been granted to group %RTgid\n",
288 pThis->Name.sun_path, gid));
289 return VINF_SUCCESS;
290 }
291 LogRel(("RTLocalIpcServerGrantGroupAccess: cannot grant IPC socket %s write permission to group %RTgid: errno=%d\n",
292 pThis->Name.sun_path, gid, errno));
293 }
294 else
295 LogRel(("RTLocalIpcServerGrantGroupAccess: cannot change IPC socket %s group ownership to %RTgid: errno=%d\n",
296 pThis->Name.sun_path, gid, errno));
297 return RTErrConvertFromErrno(errno);
298}
299
300
301/**
302 * Retains a reference to the server instance.
303 *
304 * @returns
305 * @param pThis The server instance.
306 */
307DECLINLINE(void) rtLocalIpcServerRetain(PRTLOCALIPCSERVERINT pThis)
308{
309 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
310 Assert(cRefs < UINT32_MAX / 2 && cRefs); RT_NOREF_PV(cRefs);
311}
312
313
314/**
315 * Server instance destructor.
316 *
317 * @returns VINF_OBJECT_DESTROYED
318 * @param pThis The server instance.
319 */
320static int rtLocalIpcServerDtor(PRTLOCALIPCSERVERINT pThis)
321{
322 pThis->u32Magic = ~RTLOCALIPCSERVER_MAGIC;
323 if (RTSocketRelease(pThis->hSocket) == 0)
324 Log(("rtLocalIpcServerDtor: Released socket\n"));
325 else
326 Log(("rtLocalIpcServerDtor: Socket still has references (impossible?)\n"));
327 RTCritSectDelete(&pThis->CritSect);
328 unlink(pThis->Name.sun_path);
329 RTMemFree(pThis);
330 return VINF_OBJECT_DESTROYED;
331}
332
333
334/**
335 * Releases a reference to the server instance.
336 *
337 * @returns VINF_SUCCESS if only release, VINF_OBJECT_DESTROYED if destroyed.
338 * @param pThis The server instance.
339 */
340DECLINLINE(int) rtLocalIpcServerRelease(PRTLOCALIPCSERVERINT pThis)
341{
342 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
343 Assert(cRefs < UINT32_MAX / 2);
344 if (!cRefs)
345 return rtLocalIpcServerDtor(pThis);
346 return VINF_SUCCESS;
347}
348
349
350/**
351 * The core of RTLocalIpcServerCancel, used by both the destroy and cancel APIs.
352 *
353 * @returns IPRT status code
354 * @param pThis The server instance.
355 */
356static int rtLocalIpcServerCancel(PRTLOCALIPCSERVERINT pThis)
357{
358 RTCritSectEnter(&pThis->CritSect);
359 pThis->fCancelled = true;
360 Log(("rtLocalIpcServerCancel:\n"));
361 if (pThis->hListenThread != NIL_RTTHREAD)
362 RTThreadPoke(pThis->hListenThread);
363 RTCritSectLeave(&pThis->CritSect);
364 return VINF_SUCCESS;
365}
366
367
368
369RTDECL(int) RTLocalIpcServerDestroy(RTLOCALIPCSERVER hServer)
370{
371 /*
372 * Validate input.
373 */
374 if (hServer == NIL_RTLOCALIPCSERVER)
375 return VINF_SUCCESS;
376 PRTLOCALIPCSERVERINT pThis = (PRTLOCALIPCSERVERINT)hServer;
377 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
378 AssertReturn(pThis->u32Magic == RTLOCALIPCSERVER_MAGIC, VERR_INVALID_HANDLE);
379
380 /*
381 * Invalidate the server, releasing the caller's reference to the instance
382 * data and making sure any other thread in the listen API will wake up.
383 */
384 AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, ~RTLOCALIPCSERVER_MAGIC, RTLOCALIPCSERVER_MAGIC), VERR_WRONG_ORDER);
385
386 rtLocalIpcServerCancel(pThis);
387 return rtLocalIpcServerRelease(pThis);
388}
389
390
391RTDECL(int) RTLocalIpcServerCancel(RTLOCALIPCSERVER hServer)
392{
393 /*
394 * Validate input.
395 */
396 PRTLOCALIPCSERVERINT pThis = (PRTLOCALIPCSERVERINT)hServer;
397 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
398 AssertReturn(pThis->u32Magic == RTLOCALIPCSERVER_MAGIC, VERR_INVALID_HANDLE);
399
400 /*
401 * Do the job.
402 */
403 rtLocalIpcServerRetain(pThis);
404 rtLocalIpcServerCancel(pThis);
405 rtLocalIpcServerRelease(pThis);
406 return VINF_SUCCESS;
407}
408
409
410RTDECL(int) RTLocalIpcServerListen(RTLOCALIPCSERVER hServer, PRTLOCALIPCSESSION phClientSession)
411{
412 /*
413 * Validate input.
414 */
415 PRTLOCALIPCSERVERINT pThis = (PRTLOCALIPCSERVERINT)hServer;
416 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
417 AssertReturn(pThis->u32Magic == RTLOCALIPCSERVER_MAGIC, VERR_INVALID_HANDLE);
418
419 /*
420 * Begin listening.
421 */
422 rtLocalIpcServerRetain(pThis);
423 int rc = RTCritSectEnter(&pThis->CritSect);
424 if (RT_SUCCESS(rc))
425 {
426 if (pThis->hListenThread == NIL_RTTHREAD)
427 {
428 pThis->hListenThread = RTThreadSelf();
429
430 /*
431 * The listening retry loop.
432 */
433 for (;;)
434 {
435 if (!pThis->fCancelled)
436 {
437 rc = RTCritSectLeave(&pThis->CritSect);
438 AssertRCBreak(rc);
439
440 struct sockaddr_un Addr;
441 size_t cbAddr = sizeof(Addr);
442 RTSOCKET hClient;
443 Log(("RTLocalIpcServerListen: Calling rtSocketAccept...\n"));
444 rc = rtSocketAccept(pThis->hSocket, &hClient, (struct sockaddr *)&Addr, &cbAddr);
445 Log(("RTLocalIpcServerListen: rtSocketAccept returns %Rrc.\n", rc));
446
447 int rc2 = RTCritSectEnter(&pThis->CritSect);
448 AssertRCBreakStmt(rc2, rc = RT_SUCCESS(rc) ? rc2 : rc);
449
450 if (RT_SUCCESS(rc))
451 {
452 /*
453 * Create a client session.
454 */
455 PRTLOCALIPCSESSIONINT pSession = (PRTLOCALIPCSESSIONINT)RTMemAllocZ(sizeof(*pSession));
456 if (pSession)
457 {
458 pSession->u32Magic = RTLOCALIPCSESSION_MAGIC;
459 pSession->cRefs = 1;
460 pSession->fCancelled = false;
461 pSession->fServerSide = true;
462 pSession->hSocket = hClient;
463 pSession->hReadThread = NIL_RTTHREAD;
464 pSession->hWriteThread = NIL_RTTHREAD;
465 rc = RTCritSectInit(&pSession->CritSect);
466 if (RT_SUCCESS(rc))
467 {
468 Log(("RTLocalIpcServerListen: Returning new client session: %p\n", pSession));
469 *phClientSession = pSession;
470 break;
471 }
472
473 RTMemFree(pSession);
474 }
475 else
476 rc = VERR_NO_MEMORY;
477 }
478 else if ( rc != VERR_INTERRUPTED
479 && rc != VERR_TRY_AGAIN)
480 break;
481 }
482 else
483 {
484 rc = VERR_CANCELLED;
485 break;
486 }
487 }
488
489 pThis->hListenThread = NIL_RTTHREAD;
490 }
491 else
492 {
493 AssertFailed();
494 rc = VERR_RESOURCE_BUSY;
495 }
496 int rc2 = RTCritSectLeave(&pThis->CritSect);
497 AssertStmt(RT_SUCCESS(rc2), rc = RT_SUCCESS(rc) ? rc2 : rc);
498 }
499 rtLocalIpcServerRelease(pThis);
500
501 Log(("RTLocalIpcServerListen: returns %Rrc\n", rc));
502 return rc;
503}
504
505
506RTDECL(int) RTLocalIpcSessionConnect(PRTLOCALIPCSESSION phSession, const char *pszName, uint32_t fFlags)
507{
508 /*
509 * Parameter validation.
510 */
511 AssertPtrReturn(phSession, VERR_INVALID_POINTER);
512 *phSession = NIL_RTLOCALIPCSESSION;
513
514 AssertReturn(!(fFlags & ~RTLOCALIPC_C_FLAGS_VALID_MASK), VERR_INVALID_FLAGS);
515
516 int rc = rtLocalIpcPosixValidateName(pszName, RT_BOOL(fFlags & RTLOCALIPC_C_FLAGS_NATIVE_NAME));
517 if (RT_SUCCESS(rc))
518 {
519 /*
520 * Allocate memory for the instance and initialize it.
521 */
522 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)RTMemAllocZ(sizeof(*pThis));
523 if (pThis)
524 {
525 pThis->u32Magic = RTLOCALIPCSESSION_MAGIC;
526 pThis->cRefs = 1;
527 pThis->fCancelled = false;
528 pThis->fServerSide = false;
529 pThis->hSocket = NIL_RTSOCKET;
530 pThis->hReadThread = NIL_RTTHREAD;
531 pThis->hWriteThread = NIL_RTTHREAD;
532 rc = RTCritSectInit(&pThis->CritSect);
533 if (RT_SUCCESS(rc))
534 {
535 /*
536 * Create the local (unix) socket and try connect to the server.
537 */
538 rc = rtSocketCreate(&pThis->hSocket, AF_LOCAL, SOCK_STREAM, 0 /*iProtocol*/);
539 if (RT_SUCCESS(rc))
540 {
541 RTSocketSetInheritance(pThis->hSocket, false /*fInheritable*/);
542 signal(SIGPIPE, SIG_IGN); /* Required on solaris, at least. */
543
544 struct sockaddr_un Addr;
545 uint8_t cbAddr;
546 rc = rtLocalIpcPosixConstructName(&Addr, &cbAddr, pszName, RT_BOOL(fFlags & RTLOCALIPC_C_FLAGS_NATIVE_NAME));
547 if (RT_SUCCESS(rc))
548 {
549 rc = rtSocketConnectRaw(pThis->hSocket, &Addr, cbAddr);
550 if (RT_SUCCESS(rc))
551 {
552 *phSession = pThis;
553 Log(("RTLocalIpcSessionConnect: Returns new session %p\n", pThis));
554 return VINF_SUCCESS;
555 }
556 }
557 RTSocketRelease(pThis->hSocket);
558 }
559 RTCritSectDelete(&pThis->CritSect);
560 }
561 RTMemFree(pThis);
562 }
563 else
564 rc = VERR_NO_MEMORY;
565 }
566 Log(("RTLocalIpcSessionConnect: returns %Rrc\n", rc));
567 return rc;
568}
569
570
571/**
572 * Retains a reference to the session instance.
573 *
574 * @param pThis The server instance.
575 */
576DECLINLINE(void) rtLocalIpcSessionRetain(PRTLOCALIPCSESSIONINT pThis)
577{
578 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
579 Assert(cRefs < UINT32_MAX / 2 && cRefs); RT_NOREF_PV(cRefs);
580}
581
582
583RTDECL(uint32_t) RTLocalIpcSessionRetain(RTLOCALIPCSESSION hSession)
584{
585 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
586 AssertPtrReturn(pThis, UINT32_MAX);
587 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, UINT32_MAX);
588
589 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
590 Assert(cRefs < UINT32_MAX / 2 && cRefs);
591 return cRefs;
592}
593
594
595/**
596 * Session instance destructor.
597 *
598 * @returns VINF_OBJECT_DESTROYED
599 * @param pThis The server instance.
600 */
601static int rtLocalIpcSessionDtor(PRTLOCALIPCSESSIONINT pThis)
602{
603 pThis->u32Magic = ~RTLOCALIPCSESSION_MAGIC;
604 if (RTSocketRelease(pThis->hSocket) == 0)
605 Log(("rtLocalIpcSessionDtor: Released socket\n"));
606 else
607 Log(("rtLocalIpcSessionDtor: Socket still has references (impossible?)\n"));
608 RTCritSectDelete(&pThis->CritSect);
609 RTMemFree(pThis);
610 return VINF_OBJECT_DESTROYED;
611}
612
613
614/**
615 * Releases a reference to the session instance.
616 *
617 * @returns VINF_SUCCESS or VINF_OBJECT_DESTROYED as appropriate.
618 * @param pThis The session instance.
619 */
620DECLINLINE(int) rtLocalIpcSessionRelease(PRTLOCALIPCSESSIONINT pThis)
621{
622 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
623 Assert(cRefs < UINT32_MAX / 2);
624 if (!cRefs)
625 return rtLocalIpcSessionDtor(pThis);
626 Log(("rtLocalIpcSessionRelease: %u refs left\n", cRefs));
627 return VINF_SUCCESS;
628}
629
630
631RTDECL(uint32_t) RTLocalIpcSessionRelease(RTLOCALIPCSESSION hSession)
632{
633 if (hSession == NIL_RTLOCALIPCSESSION)
634 return 0;
635
636 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
637 AssertPtrReturn(pThis, UINT32_MAX);
638 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, UINT32_MAX);
639
640 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
641 Assert(cRefs < UINT32_MAX / 2);
642 if (cRefs)
643 Log(("RTLocalIpcSessionRelease: %u refs left\n", cRefs));
644 else
645 rtLocalIpcSessionDtor(pThis);
646 return cRefs;
647}
648
649
650/**
651 * The core of RTLocalIpcSessionCancel, used by both the destroy and cancel APIs.
652 *
653 * @returns IPRT status code
654 * @param pThis The session instance.
655 */
656static int rtLocalIpcSessionCancel(PRTLOCALIPCSESSIONINT pThis)
657{
658 RTCritSectEnter(&pThis->CritSect);
659 pThis->fCancelled = true;
660 Log(("rtLocalIpcSessionCancel:\n"));
661 if (pThis->hReadThread != NIL_RTTHREAD)
662 RTThreadPoke(pThis->hReadThread);
663 if (pThis->hWriteThread != NIL_RTTHREAD)
664 RTThreadPoke(pThis->hWriteThread);
665 RTCritSectLeave(&pThis->CritSect);
666 return VINF_SUCCESS;
667}
668
669
670RTDECL(int) RTLocalIpcSessionClose(RTLOCALIPCSESSION hSession)
671{
672 /*
673 * Validate input.
674 */
675 if (hSession == NIL_RTLOCALIPCSESSION)
676 return VINF_SUCCESS;
677 PRTLOCALIPCSESSIONINT pThis = hSession;
678 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
679 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
680
681 /*
682 * Invalidate the session, releasing the caller's reference to the instance
683 * data and making sure any other thread in the listen API will wake up.
684 */
685 Log(("RTLocalIpcSessionClose:\n"));
686
687 rtLocalIpcSessionCancel(pThis);
688 return rtLocalIpcSessionRelease(pThis);
689}
690
691
692RTDECL(int) RTLocalIpcSessionCancel(RTLOCALIPCSESSION hSession)
693{
694 /*
695 * Validate input.
696 */
697 PRTLOCALIPCSESSIONINT pThis = hSession;
698 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
699 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
700
701 /*
702 * Do the job.
703 */
704 rtLocalIpcSessionRetain(pThis);
705 rtLocalIpcSessionCancel(pThis);
706 rtLocalIpcSessionRelease(pThis);
707 return VINF_SUCCESS;
708}
709
710
711/**
712 * Checks if the socket has has a HUP condition after reading zero bytes.
713 *
714 * @returns true if HUP, false if no.
715 * @param pThis The IPC session handle.
716 */
717static bool rtLocalIpcPosixHasHup(PRTLOCALIPCSESSIONINT pThis)
718{
719 int fdNative = RTSocketToNative(pThis->hSocket);
720
721#if !defined(RT_OS_OS2) && !defined(RT_OS_SOLARIS)
722 struct pollfd PollFd;
723 RT_ZERO(PollFd);
724 PollFd.fd = fdNative;
725 PollFd.events = POLLHUP | POLLERR;
726 if (poll(&PollFd, 1, 0) <= 0)
727 return false;
728 if (!(PollFd.revents & (POLLHUP | POLLERR)))
729 return false;
730#else /* RT_OS_OS2 || RT_OS_SOLARIS */
731 /*
732 * OS/2: No native poll, do zero byte send to check for EPIPE.
733 * Solaris: We don't get POLLHUP.
734 */
735 uint8_t bDummy;
736 ssize_t rcSend = send(fdNative, &bDummy, 0, 0);
737 if (rcSend >= 0 || (errno != EPIPE && errno != ECONNRESET))
738 return false;
739#endif /* RT_OS_OS2 || RT_OS_SOLARIS */
740
741 /*
742 * We've established EPIPE. Now make sure there aren't any last bytes to
743 * read that came in between the recv made by the caller and the disconnect.
744 */
745 uint8_t bPeek;
746 ssize_t rcRecv = recv(fdNative, &bPeek, 1, MSG_DONTWAIT | MSG_PEEK);
747 return rcRecv <= 0;
748}
749
750
751RTDECL(int) RTLocalIpcSessionRead(RTLOCALIPCSESSION hSession, void *pvBuf, size_t cbToRead, size_t *pcbRead)
752{
753 /*
754 * Validate input.
755 */
756 PRTLOCALIPCSESSIONINT pThis = hSession;
757 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
758 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
759
760 /*
761 * Do the job.
762 */
763 rtLocalIpcSessionRetain(pThis);
764
765 int rc = RTCritSectEnter(&pThis->CritSect);
766 if (RT_SUCCESS(rc))
767 {
768 if (pThis->hReadThread == NIL_RTTHREAD)
769 {
770 pThis->hReadThread = RTThreadSelf();
771
772 for (;;)
773 {
774 if (!pThis->fCancelled)
775 {
776 rc = RTCritSectLeave(&pThis->CritSect);
777 AssertRCBreak(rc);
778
779 rc = RTSocketRead(pThis->hSocket, pvBuf, cbToRead, pcbRead);
780
781 /* Detect broken pipe. */
782 if (rc == VINF_SUCCESS)
783 {
784 if (!pcbRead || *pcbRead)
785 { /* likely */ }
786 else if (rtLocalIpcPosixHasHup(pThis))
787 rc = VERR_BROKEN_PIPE;
788 }
789 else if (rc == VERR_NET_CONNECTION_RESET_BY_PEER || rc == VERR_NET_SHUTDOWN)
790 rc = VERR_BROKEN_PIPE;
791
792 int rc2 = RTCritSectEnter(&pThis->CritSect);
793 AssertRCBreakStmt(rc2, rc = RT_SUCCESS(rc) ? rc2 : rc);
794
795 if ( rc == VERR_INTERRUPTED
796 || rc == VERR_TRY_AGAIN)
797 continue;
798 }
799 else
800 rc = VERR_CANCELLED;
801 break;
802 }
803
804 pThis->hReadThread = NIL_RTTHREAD;
805 }
806 int rc2 = RTCritSectLeave(&pThis->CritSect);
807 AssertStmt(RT_SUCCESS(rc2), rc = RT_SUCCESS(rc) ? rc2 : rc);
808 }
809
810 rtLocalIpcSessionRelease(pThis);
811 return rc;
812}
813
814
815RTDECL(int) RTLocalIpcSessionReadNB(RTLOCALIPCSESSION hSession, void *pvBuf, size_t cbToRead, size_t *pcbRead)
816{
817 /*
818 * Validate input.
819 */
820 PRTLOCALIPCSESSIONINT pThis = hSession;
821 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
822 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
823
824 /*
825 * Do the job.
826 */
827 rtLocalIpcSessionRetain(pThis);
828
829 int rc = RTCritSectEnter(&pThis->CritSect);
830 if (RT_SUCCESS(rc))
831 {
832 if (pThis->hReadThread == NIL_RTTHREAD)
833 {
834 pThis->hReadThread = RTThreadSelf(); /* not really required, but whatever. */
835
836 for (;;)
837 {
838 if (!pThis->fCancelled)
839 {
840 rc = RTSocketReadNB(pThis->hSocket, pvBuf, cbToRead, pcbRead);
841
842 /* Detect broken pipe. */
843 if (rc == VINF_SUCCESS)
844 {
845 if (!pcbRead || *pcbRead)
846 { /* likely */ }
847 else if (rtLocalIpcPosixHasHup(pThis))
848 rc = VERR_BROKEN_PIPE;
849 }
850 else if (rc == VERR_NET_CONNECTION_RESET_BY_PEER || rc == VERR_NET_SHUTDOWN)
851 rc = VERR_BROKEN_PIPE;
852
853 if (rc == VERR_INTERRUPTED)
854 continue;
855 }
856 else
857 rc = VERR_CANCELLED;
858 break;
859 }
860
861 pThis->hReadThread = NIL_RTTHREAD;
862 }
863 int rc2 = RTCritSectLeave(&pThis->CritSect);
864 AssertStmt(RT_SUCCESS(rc2), rc = RT_SUCCESS(rc) ? rc2 : rc);
865 }
866
867 rtLocalIpcSessionRelease(pThis);
868 return rc;
869}
870
871
872RTDECL(int) RTLocalIpcSessionWrite(RTLOCALIPCSESSION hSession, const void *pvBuf, size_t cbToWrite)
873{
874 /*
875 * Validate input.
876 */
877 PRTLOCALIPCSESSIONINT pThis = hSession;
878 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
879 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
880
881 /*
882 * Do the job.
883 */
884 rtLocalIpcSessionRetain(pThis);
885
886 int rc = RTCritSectEnter(&pThis->CritSect);
887 if (RT_SUCCESS(rc))
888 {
889 if (pThis->hWriteThread == NIL_RTTHREAD)
890 {
891 pThis->hWriteThread = RTThreadSelf();
892
893 for (;;)
894 {
895 if (!pThis->fCancelled)
896 {
897 rc = RTCritSectLeave(&pThis->CritSect);
898 AssertRCBreak(rc);
899
900 rc = RTSocketWrite(pThis->hSocket, pvBuf, cbToWrite);
901
902 int rc2 = RTCritSectEnter(&pThis->CritSect);
903 AssertRCBreakStmt(rc2, rc = RT_SUCCESS(rc) ? rc2 : rc);
904
905 if ( rc == VERR_INTERRUPTED
906 || rc == VERR_TRY_AGAIN)
907 continue;
908 }
909 else
910 rc = VERR_CANCELLED;
911 break;
912 }
913
914 pThis->hWriteThread = NIL_RTTHREAD;
915 }
916 int rc2 = RTCritSectLeave(&pThis->CritSect);
917 AssertStmt(RT_SUCCESS(rc2), rc = RT_SUCCESS(rc) ? rc2 : rc);
918 }
919
920 rtLocalIpcSessionRelease(pThis);
921 return rc;
922}
923
924
925RTDECL(int) RTLocalIpcSessionFlush(RTLOCALIPCSESSION hSession)
926{
927 /*
928 * Validate input.
929 */
930 PRTLOCALIPCSESSIONINT pThis = hSession;
931 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
932 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
933
934 /*
935 * This is a no-op because apparently write doesn't return until the
936 * result is read. At least that's what the reply to a 2003-04-08 LKML
937 * posting title "fsync() on unix domain sockets?" indicates.
938 *
939 * For conformity, make sure there isn't any active writes concurrent to this call.
940 */
941 rtLocalIpcSessionRetain(pThis);
942
943 int rc = RTCritSectEnter(&pThis->CritSect);
944 if (RT_SUCCESS(rc))
945 {
946 if (pThis->hWriteThread == NIL_RTTHREAD)
947 rc = RTCritSectLeave(&pThis->CritSect);
948 else
949 {
950 rc = RTCritSectLeave(&pThis->CritSect);
951 if (RT_SUCCESS(rc))
952 rc = VERR_RESOURCE_BUSY;
953 }
954 }
955
956 rtLocalIpcSessionRelease(pThis);
957 return rc;
958}
959
960
961RTDECL(int) RTLocalIpcSessionWaitForData(RTLOCALIPCSESSION hSession, uint32_t cMillies)
962{
963 /*
964 * Validate input.
965 */
966 PRTLOCALIPCSESSIONINT pThis = hSession;
967 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
968 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
969
970 /*
971 * Do the job.
972 */
973 rtLocalIpcSessionRetain(pThis);
974
975 int rc = RTCritSectEnter(&pThis->CritSect);
976 if (RT_SUCCESS(rc))
977 {
978 if (pThis->hReadThread == NIL_RTTHREAD)
979 {
980 pThis->hReadThread = RTThreadSelf();
981 uint64_t const msStart = RTTimeMilliTS();
982 RTMSINTERVAL const cMsOriginalTimeout = cMillies;
983
984 for (;;)
985 {
986 if (!pThis->fCancelled)
987 {
988 rc = RTCritSectLeave(&pThis->CritSect);
989 AssertRCBreak(rc);
990
991 uint32_t fEvents = 0;
992#ifdef RT_OS_OS2
993 /* This doesn't give us any error condition on hangup, so use HUP check. */
994 Log(("RTLocalIpcSessionWaitForData: Calling RTSocketSelectOneEx...\n"));
995 rc = RTSocketSelectOneEx(pThis->hSocket, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, &fEvents, cMillies);
996 Log(("RTLocalIpcSessionWaitForData: RTSocketSelectOneEx returns %Rrc, fEvents=%#x\n", rc, fEvents));
997 if (RT_SUCCESS(rc) && fEvents == RTPOLL_EVT_READ && rtLocalIpcPosixHasHup(pThis))
998 rc = VERR_BROKEN_PIPE;
999#else
1000/** @todo RTSocketPoll? */
1001 /* POLLHUP will be set on hangup. */
1002 struct pollfd PollFd;
1003 RT_ZERO(PollFd);
1004 PollFd.fd = RTSocketToNative(pThis->hSocket);
1005 PollFd.events = POLLHUP | POLLERR | POLLIN;
1006 Log(("RTLocalIpcSessionWaitForData: Calling poll...\n"));
1007 int cFds = poll(&PollFd, 1, cMillies == RT_INDEFINITE_WAIT ? -1 : cMillies);
1008 if (cFds >= 1)
1009 {
1010 /* Linux & Darwin sets both POLLIN and POLLHUP when the pipe is
1011 broken and but no more data to read. Google hints at NetBSD
1012 returning more sane values (POLLIN till no more data, then
1013 POLLHUP). Solairs OTOH, doesn't ever seem to return POLLHUP. */
1014 fEvents = RTPOLL_EVT_READ;
1015 if ( (PollFd.revents & (POLLHUP | POLLERR))
1016 && !(PollFd.revents & POLLIN))
1017 fEvents = RTPOLL_EVT_ERROR;
1018# if defined(RT_OS_SOLARIS)
1019 else if (PollFd.revents & POLLIN)
1020# else
1021 else if ((PollFd.revents & (POLLIN | POLLHUP)) == (POLLIN | POLLHUP))
1022# endif
1023 {
1024 /* Check if there is actually data available. */
1025 uint8_t bPeek;
1026 ssize_t rcRecv = recv(PollFd.fd, &bPeek, 1, MSG_DONTWAIT | MSG_PEEK);
1027 if (rcRecv <= 0)
1028 fEvents = RTPOLL_EVT_ERROR;
1029 }
1030 rc = VINF_SUCCESS;
1031 }
1032 else if (rc == 0)
1033 rc = VERR_TIMEOUT;
1034 else
1035 rc = RTErrConvertFromErrno(errno);
1036 Log(("RTLocalIpcSessionWaitForData: poll returns %u (rc=%d), revents=%#x\n", cFds, rc, PollFd.revents));
1037#endif
1038
1039 int rc2 = RTCritSectEnter(&pThis->CritSect);
1040 AssertRCBreakStmt(rc2, rc = RT_SUCCESS(rc) ? rc2 : rc);
1041
1042 if (RT_SUCCESS(rc))
1043 {
1044 if (pThis->fCancelled)
1045 rc = VERR_CANCELLED;
1046 else if (fEvents & RTPOLL_EVT_ERROR)
1047 rc = VERR_BROKEN_PIPE;
1048 }
1049 else if ( rc == VERR_INTERRUPTED
1050 || rc == VERR_TRY_AGAIN)
1051 {
1052 /* Recalc cMillies. */
1053 if (cMsOriginalTimeout != RT_INDEFINITE_WAIT)
1054 {
1055 uint64_t cMsElapsed = RTTimeMilliTS() - msStart;
1056 cMillies = cMsElapsed >= cMsOriginalTimeout ? 0 : cMsOriginalTimeout - (RTMSINTERVAL)cMsElapsed;
1057 }
1058 continue;
1059 }
1060 }
1061 else
1062 rc = VERR_CANCELLED;
1063 break;
1064 }
1065
1066 pThis->hReadThread = NIL_RTTHREAD;
1067 }
1068 int rc2 = RTCritSectLeave(&pThis->CritSect);
1069 AssertStmt(RT_SUCCESS(rc2), rc = RT_SUCCESS(rc) ? rc2 : rc);
1070 }
1071
1072 rtLocalIpcSessionRelease(pThis);
1073 return rc;
1074}
1075
1076
1077/**
1078 * Get IPC session socket peer credentials.
1079 *
1080 * @returns IPRT status code.
1081 * @param hSession IPC session handle.
1082 * @param pProcess Where to return the remote peer's PID (can be NULL).
1083 * @param pUid Where to return the remote peer's UID (can be NULL).
1084 * @param pGid Where to return the remote peer's GID (can be NULL).
1085 */
1086static int rtLocalIpcSessionQueryUcred(RTLOCALIPCSESSION hSession, PRTPROCESS pProcess, PRTUID pUid, PRTGID pGid)
1087{
1088 PRTLOCALIPCSESSIONINT pThis = hSession;
1089 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1090 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
1091
1092#if defined(RT_OS_LINUX)
1093 struct ucred PeerCred = { (pid_t)NIL_RTPROCESS, (uid_t)NIL_RTUID, (gid_t)NIL_RTGID };
1094 socklen_t cbPeerCred = sizeof(PeerCred);
1095
1096 rtLocalIpcSessionRetain(pThis);
1097
1098 int rc = RTCritSectEnter(&pThis->CritSect);;
1099 if (RT_SUCCESS(rc))
1100 {
1101 if (getsockopt(RTSocketToNative(pThis->hSocket), SOL_SOCKET, SO_PEERCRED, &PeerCred, &cbPeerCred) >= 0)
1102 {
1103 if (pProcess)
1104 *pProcess = PeerCred.pid;
1105 if (pUid)
1106 *pUid = PeerCred.uid;
1107 if (pGid)
1108 *pGid = PeerCred.gid;
1109 rc = VINF_SUCCESS;
1110 }
1111 else
1112 {
1113 rc = RTErrConvertFromErrno(errno);
1114 }
1115
1116 int rc2 = RTCritSectLeave(&pThis->CritSect);
1117 AssertStmt(RT_SUCCESS(rc2), rc = RT_SUCCESS(rc) ? rc2 : rc);
1118 }
1119
1120 rtLocalIpcSessionRelease(pThis);
1121
1122 return rc;
1123
1124#else
1125 /** @todo Implement on other platforms too (mostly platform specific this).
1126 * Solaris: getpeerucred? Darwin: LOCALPEERCRED or getpeereid? */
1127 RT_NOREF(pProcess, pUid, pGid);
1128 return VERR_NOT_SUPPORTED;
1129#endif
1130}
1131
1132
1133RTDECL(int) RTLocalIpcSessionQueryProcess(RTLOCALIPCSESSION hSession, PRTPROCESS pProcess)
1134{
1135 return rtLocalIpcSessionQueryUcred(hSession, pProcess, NULL, NULL);
1136}
1137
1138
1139RTDECL(int) RTLocalIpcSessionQueryUserId(RTLOCALIPCSESSION hSession, PRTUID pUid)
1140{
1141 return rtLocalIpcSessionQueryUcred(hSession, NULL, pUid, NULL);
1142}
1143
1144RTDECL(int) RTLocalIpcSessionQueryGroupId(RTLOCALIPCSESSION hSession, PRTGID pGid)
1145{
1146 return rtLocalIpcSessionQueryUcred(hSession, NULL, NULL, pGid);
1147}
1148
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