VirtualBox

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

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 38.6 KB
Line 
1/* $Id: localipc-posix.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * IPRT - Local IPC Server & Client, Posix.
4 */
5
6/*
7 * Copyright (C) 2006-2023 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 rc = VERR_NO_MEMORY;
504 }
505 else if ( rc != VERR_INTERRUPTED
506 && rc != VERR_TRY_AGAIN)
507 break;
508 }
509 else
510 {
511 rc = VERR_CANCELLED;
512 break;
513 }
514 }
515
516 pThis->hListenThread = NIL_RTTHREAD;
517 }
518 else
519 {
520 AssertFailed();
521 rc = VERR_RESOURCE_BUSY;
522 }
523 int rc2 = RTCritSectLeave(&pThis->CritSect);
524 AssertStmt(RT_SUCCESS(rc2), rc = RT_SUCCESS(rc) ? rc2 : rc);
525 }
526 rtLocalIpcServerRelease(pThis);
527
528 Log(("RTLocalIpcServerListen: returns %Rrc\n", rc));
529 return rc;
530}
531
532
533RTDECL(int) RTLocalIpcSessionConnect(PRTLOCALIPCSESSION phSession, const char *pszName, uint32_t fFlags)
534{
535 /*
536 * Parameter validation.
537 */
538 AssertPtrReturn(phSession, VERR_INVALID_POINTER);
539 *phSession = NIL_RTLOCALIPCSESSION;
540
541 AssertReturn(!(fFlags & ~RTLOCALIPC_C_FLAGS_VALID_MASK), VERR_INVALID_FLAGS);
542
543 int rc = rtLocalIpcPosixValidateName(pszName, RT_BOOL(fFlags & RTLOCALIPC_C_FLAGS_NATIVE_NAME));
544 if (RT_SUCCESS(rc))
545 {
546 /*
547 * Allocate memory for the instance and initialize it.
548 */
549 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)RTMemAllocZ(sizeof(*pThis));
550 if (pThis)
551 {
552 pThis->u32Magic = RTLOCALIPCSESSION_MAGIC;
553 pThis->cRefs = 1;
554 pThis->fCancelled = false;
555 pThis->fServerSide = false;
556 pThis->hSocket = NIL_RTSOCKET;
557 pThis->hReadThread = NIL_RTTHREAD;
558 pThis->hWriteThread = NIL_RTTHREAD;
559 rc = RTCritSectInit(&pThis->CritSect);
560 if (RT_SUCCESS(rc))
561 {
562 /*
563 * Create the local (unix) socket and try connect to the server.
564 */
565 rc = rtSocketCreate(&pThis->hSocket, AF_LOCAL, SOCK_STREAM, 0 /*iProtocol*/, false /*fInheritable*/);
566 if (RT_SUCCESS(rc))
567 {
568 signal(SIGPIPE, SIG_IGN); /* Required on solaris, at least. */
569
570 struct sockaddr_un Addr;
571 uint8_t cbAddr;
572 rc = rtLocalIpcPosixConstructName(&Addr, &cbAddr, pszName, RT_BOOL(fFlags & RTLOCALIPC_C_FLAGS_NATIVE_NAME));
573 if (RT_SUCCESS(rc))
574 {
575 rc = rtSocketConnectRaw(pThis->hSocket, &Addr, cbAddr);
576 if (RT_SUCCESS(rc))
577 {
578 *phSession = pThis;
579 Log(("RTLocalIpcSessionConnect: Returns new session %p\n", pThis));
580 return VINF_SUCCESS;
581 }
582 }
583 RTSocketRelease(pThis->hSocket);
584 }
585 RTCritSectDelete(&pThis->CritSect);
586 }
587 RTMemFree(pThis);
588 }
589 else
590 rc = VERR_NO_MEMORY;
591 }
592 Log(("RTLocalIpcSessionConnect: returns %Rrc\n", rc));
593 return rc;
594}
595
596
597/**
598 * Retains a reference to the session instance.
599 *
600 * @param pThis The server instance.
601 */
602DECLINLINE(void) rtLocalIpcSessionRetain(PRTLOCALIPCSESSIONINT pThis)
603{
604 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
605 Assert(cRefs < UINT32_MAX / 2 && cRefs); RT_NOREF_PV(cRefs);
606}
607
608
609RTDECL(uint32_t) RTLocalIpcSessionRetain(RTLOCALIPCSESSION hSession)
610{
611 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
612 AssertPtrReturn(pThis, UINT32_MAX);
613 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, UINT32_MAX);
614
615 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
616 Assert(cRefs < UINT32_MAX / 2 && cRefs);
617 return cRefs;
618}
619
620
621/**
622 * Session instance destructor.
623 *
624 * @returns VINF_OBJECT_DESTROYED
625 * @param pThis The server instance.
626 */
627static int rtLocalIpcSessionDtor(PRTLOCALIPCSESSIONINT pThis)
628{
629 pThis->u32Magic = ~RTLOCALIPCSESSION_MAGIC;
630 if (RTSocketRelease(pThis->hSocket) == 0)
631 Log(("rtLocalIpcSessionDtor: Released socket\n"));
632 else
633 Log(("rtLocalIpcSessionDtor: Socket still has references (impossible?)\n"));
634 RTCritSectDelete(&pThis->CritSect);
635 RTMemFree(pThis);
636 return VINF_OBJECT_DESTROYED;
637}
638
639
640/**
641 * Releases a reference to the session instance.
642 *
643 * @returns VINF_SUCCESS or VINF_OBJECT_DESTROYED as appropriate.
644 * @param pThis The session instance.
645 */
646DECLINLINE(int) rtLocalIpcSessionRelease(PRTLOCALIPCSESSIONINT pThis)
647{
648 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
649 Assert(cRefs < UINT32_MAX / 2);
650 if (!cRefs)
651 return rtLocalIpcSessionDtor(pThis);
652 Log(("rtLocalIpcSessionRelease: %u refs left\n", cRefs));
653 return VINF_SUCCESS;
654}
655
656
657RTDECL(uint32_t) RTLocalIpcSessionRelease(RTLOCALIPCSESSION hSession)
658{
659 if (hSession == NIL_RTLOCALIPCSESSION)
660 return 0;
661
662 PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
663 AssertPtrReturn(pThis, UINT32_MAX);
664 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, UINT32_MAX);
665
666 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
667 Assert(cRefs < UINT32_MAX / 2);
668 if (cRefs)
669 Log(("RTLocalIpcSessionRelease: %u refs left\n", cRefs));
670 else
671 rtLocalIpcSessionDtor(pThis);
672 return cRefs;
673}
674
675
676/**
677 * The core of RTLocalIpcSessionCancel, used by both the destroy and cancel APIs.
678 *
679 * @returns IPRT status code
680 * @param pThis The session instance.
681 */
682static int rtLocalIpcSessionCancel(PRTLOCALIPCSESSIONINT pThis)
683{
684 RTCritSectEnter(&pThis->CritSect);
685 pThis->fCancelled = true;
686 Log(("rtLocalIpcSessionCancel:\n"));
687 if (pThis->hReadThread != NIL_RTTHREAD)
688 RTThreadPoke(pThis->hReadThread);
689 if (pThis->hWriteThread != NIL_RTTHREAD)
690 RTThreadPoke(pThis->hWriteThread);
691 RTCritSectLeave(&pThis->CritSect);
692 return VINF_SUCCESS;
693}
694
695
696RTDECL(int) RTLocalIpcSessionClose(RTLOCALIPCSESSION hSession)
697{
698 /*
699 * Validate input.
700 */
701 if (hSession == NIL_RTLOCALIPCSESSION)
702 return VINF_SUCCESS;
703 PRTLOCALIPCSESSIONINT pThis = hSession;
704 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
705 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
706
707 /*
708 * Invalidate the session, releasing the caller's reference to the instance
709 * data and making sure any other thread in the listen API will wake up.
710 */
711 Log(("RTLocalIpcSessionClose:\n"));
712
713 rtLocalIpcSessionCancel(pThis);
714 return rtLocalIpcSessionRelease(pThis);
715}
716
717
718RTDECL(int) RTLocalIpcSessionCancel(RTLOCALIPCSESSION hSession)
719{
720 /*
721 * Validate input.
722 */
723 PRTLOCALIPCSESSIONINT pThis = hSession;
724 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
725 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
726
727 /*
728 * Do the job.
729 */
730 rtLocalIpcSessionRetain(pThis);
731 rtLocalIpcSessionCancel(pThis);
732 rtLocalIpcSessionRelease(pThis);
733 return VINF_SUCCESS;
734}
735
736
737/**
738 * Checks if the socket has has a HUP condition after reading zero bytes.
739 *
740 * @returns true if HUP, false if no.
741 * @param pThis The IPC session handle.
742 */
743static bool rtLocalIpcPosixHasHup(PRTLOCALIPCSESSIONINT pThis)
744{
745 int fdNative = RTSocketToNative(pThis->hSocket);
746
747#if !defined(RT_OS_OS2) && !defined(RT_OS_SOLARIS)
748 struct pollfd PollFd;
749 RT_ZERO(PollFd);
750 PollFd.fd = fdNative;
751 PollFd.events = POLLHUP | POLLERR;
752 if (poll(&PollFd, 1, 0) <= 0)
753 return false;
754 if (!(PollFd.revents & (POLLHUP | POLLERR)))
755 return false;
756#else /* RT_OS_OS2 || RT_OS_SOLARIS */
757 /*
758 * OS/2: No native poll, do zero byte send to check for EPIPE.
759 * Solaris: We don't get POLLHUP.
760 */
761 uint8_t bDummy;
762 ssize_t rcSend = send(fdNative, &bDummy, 0, 0);
763 if (rcSend >= 0 || (errno != EPIPE && errno != ECONNRESET))
764 return false;
765#endif /* RT_OS_OS2 || RT_OS_SOLARIS */
766
767 /*
768 * We've established EPIPE. Now make sure there aren't any last bytes to
769 * read that came in between the recv made by the caller and the disconnect.
770 */
771 uint8_t bPeek;
772 ssize_t rcRecv = recv(fdNative, &bPeek, 1, MSG_DONTWAIT | MSG_PEEK);
773 return rcRecv <= 0;
774}
775
776
777RTDECL(int) RTLocalIpcSessionRead(RTLOCALIPCSESSION hSession, void *pvBuf, size_t cbToRead, size_t *pcbRead)
778{
779 /*
780 * Validate input.
781 */
782 PRTLOCALIPCSESSIONINT pThis = hSession;
783 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
784 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
785
786 /*
787 * Do the job.
788 */
789 rtLocalIpcSessionRetain(pThis);
790
791 int rc = RTCritSectEnter(&pThis->CritSect);
792 if (RT_SUCCESS(rc))
793 {
794 if (pThis->hReadThread == NIL_RTTHREAD)
795 {
796 pThis->hReadThread = RTThreadSelf();
797
798 for (;;)
799 {
800 if (!pThis->fCancelled)
801 {
802 rc = RTCritSectLeave(&pThis->CritSect);
803 AssertRCBreak(rc);
804
805 rc = RTSocketRead(pThis->hSocket, pvBuf, cbToRead, pcbRead);
806
807 /* Detect broken pipe. */
808 if (rc == VINF_SUCCESS)
809 {
810 if (!pcbRead || *pcbRead)
811 { /* likely */ }
812 else if (rtLocalIpcPosixHasHup(pThis))
813 rc = VERR_BROKEN_PIPE;
814 }
815 else if (rc == VERR_NET_CONNECTION_RESET_BY_PEER || rc == VERR_NET_SHUTDOWN)
816 rc = VERR_BROKEN_PIPE;
817
818 int rc2 = RTCritSectEnter(&pThis->CritSect);
819 AssertRCBreakStmt(rc2, rc = RT_SUCCESS(rc) ? rc2 : rc);
820
821 if ( rc == VERR_INTERRUPTED
822 || rc == VERR_TRY_AGAIN)
823 continue;
824 }
825 else
826 rc = VERR_CANCELLED;
827 break;
828 }
829
830 pThis->hReadThread = NIL_RTTHREAD;
831 }
832 int rc2 = RTCritSectLeave(&pThis->CritSect);
833 AssertStmt(RT_SUCCESS(rc2), rc = RT_SUCCESS(rc) ? rc2 : rc);
834 }
835
836 rtLocalIpcSessionRelease(pThis);
837 return rc;
838}
839
840
841RTDECL(int) RTLocalIpcSessionReadNB(RTLOCALIPCSESSION hSession, void *pvBuf, size_t cbToRead, size_t *pcbRead)
842{
843 /*
844 * Validate input.
845 */
846 PRTLOCALIPCSESSIONINT pThis = hSession;
847 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
848 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
849
850 /*
851 * Do the job.
852 */
853 rtLocalIpcSessionRetain(pThis);
854
855 int rc = RTCritSectEnter(&pThis->CritSect);
856 if (RT_SUCCESS(rc))
857 {
858 if (pThis->hReadThread == NIL_RTTHREAD)
859 {
860 pThis->hReadThread = RTThreadSelf(); /* not really required, but whatever. */
861
862 for (;;)
863 {
864 if (!pThis->fCancelled)
865 {
866 rc = RTSocketReadNB(pThis->hSocket, pvBuf, cbToRead, pcbRead);
867
868 /* Detect broken pipe. */
869 if (rc == VINF_SUCCESS)
870 {
871 if (!pcbRead || *pcbRead)
872 { /* likely */ }
873 else if (rtLocalIpcPosixHasHup(pThis))
874 rc = VERR_BROKEN_PIPE;
875 }
876 else if (rc == VERR_NET_CONNECTION_RESET_BY_PEER || rc == VERR_NET_SHUTDOWN)
877 rc = VERR_BROKEN_PIPE;
878
879 if (rc == VERR_INTERRUPTED)
880 continue;
881 }
882 else
883 rc = VERR_CANCELLED;
884 break;
885 }
886
887 pThis->hReadThread = NIL_RTTHREAD;
888 }
889 int rc2 = RTCritSectLeave(&pThis->CritSect);
890 AssertStmt(RT_SUCCESS(rc2), rc = RT_SUCCESS(rc) ? rc2 : rc);
891 }
892
893 rtLocalIpcSessionRelease(pThis);
894 return rc;
895}
896
897
898RTDECL(int) RTLocalIpcSessionWrite(RTLOCALIPCSESSION hSession, const void *pvBuf, size_t cbToWrite)
899{
900 /*
901 * Validate input.
902 */
903 PRTLOCALIPCSESSIONINT pThis = hSession;
904 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
905 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
906
907 /*
908 * Do the job.
909 */
910 rtLocalIpcSessionRetain(pThis);
911
912 int rc = RTCritSectEnter(&pThis->CritSect);
913 if (RT_SUCCESS(rc))
914 {
915 if (pThis->hWriteThread == NIL_RTTHREAD)
916 {
917 pThis->hWriteThread = RTThreadSelf();
918
919 for (;;)
920 {
921 if (!pThis->fCancelled)
922 {
923 rc = RTCritSectLeave(&pThis->CritSect);
924 AssertRCBreak(rc);
925
926 rc = RTSocketWrite(pThis->hSocket, pvBuf, cbToWrite);
927
928 int rc2 = RTCritSectEnter(&pThis->CritSect);
929 AssertRCBreakStmt(rc2, rc = RT_SUCCESS(rc) ? rc2 : rc);
930
931 if ( rc == VERR_INTERRUPTED
932 || rc == VERR_TRY_AGAIN)
933 continue;
934 }
935 else
936 rc = VERR_CANCELLED;
937 break;
938 }
939
940 pThis->hWriteThread = NIL_RTTHREAD;
941 }
942 int rc2 = RTCritSectLeave(&pThis->CritSect);
943 AssertStmt(RT_SUCCESS(rc2), rc = RT_SUCCESS(rc) ? rc2 : rc);
944 }
945
946 rtLocalIpcSessionRelease(pThis);
947 return rc;
948}
949
950
951RTDECL(int) RTLocalIpcSessionFlush(RTLOCALIPCSESSION hSession)
952{
953 /*
954 * Validate input.
955 */
956 PRTLOCALIPCSESSIONINT pThis = hSession;
957 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
958 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
959
960 /*
961 * This is a no-op because apparently write doesn't return until the
962 * result is read. At least that's what the reply to a 2003-04-08 LKML
963 * posting title "fsync() on unix domain sockets?" indicates.
964 *
965 * For conformity, make sure there isn't any active writes concurrent to this call.
966 */
967 rtLocalIpcSessionRetain(pThis);
968
969 int rc = RTCritSectEnter(&pThis->CritSect);
970 if (RT_SUCCESS(rc))
971 {
972 if (pThis->hWriteThread == NIL_RTTHREAD)
973 rc = RTCritSectLeave(&pThis->CritSect);
974 else
975 {
976 rc = RTCritSectLeave(&pThis->CritSect);
977 if (RT_SUCCESS(rc))
978 rc = VERR_RESOURCE_BUSY;
979 }
980 }
981
982 rtLocalIpcSessionRelease(pThis);
983 return rc;
984}
985
986
987RTDECL(int) RTLocalIpcSessionWaitForData(RTLOCALIPCSESSION hSession, uint32_t cMillies)
988{
989 /*
990 * Validate input.
991 */
992 PRTLOCALIPCSESSIONINT pThis = hSession;
993 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
994 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
995
996 /*
997 * Do the job.
998 */
999 rtLocalIpcSessionRetain(pThis);
1000
1001 int rc = RTCritSectEnter(&pThis->CritSect);
1002 if (RT_SUCCESS(rc))
1003 {
1004 if (pThis->hReadThread == NIL_RTTHREAD)
1005 {
1006 pThis->hReadThread = RTThreadSelf();
1007 uint64_t const msStart = RTTimeMilliTS();
1008 RTMSINTERVAL const cMsOriginalTimeout = cMillies;
1009
1010 for (;;)
1011 {
1012 if (!pThis->fCancelled)
1013 {
1014 rc = RTCritSectLeave(&pThis->CritSect);
1015 AssertRCBreak(rc);
1016
1017 uint32_t fEvents = 0;
1018#ifdef RT_OS_OS2
1019 /* This doesn't give us any error condition on hangup, so use HUP check. */
1020 Log(("RTLocalIpcSessionWaitForData: Calling RTSocketSelectOneEx...\n"));
1021 rc = RTSocketSelectOneEx(pThis->hSocket, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, &fEvents, cMillies);
1022 Log(("RTLocalIpcSessionWaitForData: RTSocketSelectOneEx returns %Rrc, fEvents=%#x\n", rc, fEvents));
1023 if (RT_SUCCESS(rc) && fEvents == RTPOLL_EVT_READ && rtLocalIpcPosixHasHup(pThis))
1024 rc = VERR_BROKEN_PIPE;
1025#else
1026/** @todo RTSocketPoll? */
1027 /* POLLHUP will be set on hangup. */
1028 struct pollfd PollFd;
1029 RT_ZERO(PollFd);
1030 PollFd.fd = RTSocketToNative(pThis->hSocket);
1031 PollFd.events = POLLHUP | POLLERR | POLLIN;
1032 Log(("RTLocalIpcSessionWaitForData: Calling poll...\n"));
1033 int cFds = poll(&PollFd, 1, cMillies == RT_INDEFINITE_WAIT ? -1 : (int)cMillies);
1034 if (cFds >= 1)
1035 {
1036 /* Linux & Darwin sets both POLLIN and POLLHUP when the pipe is
1037 broken and but no more data to read. Google hints at NetBSD
1038 returning more sane values (POLLIN till no more data, then
1039 POLLHUP). Solairs OTOH, doesn't ever seem to return POLLHUP. */
1040 fEvents = RTPOLL_EVT_READ;
1041 if ( (PollFd.revents & (POLLHUP | POLLERR))
1042 && !(PollFd.revents & POLLIN))
1043 fEvents = RTPOLL_EVT_ERROR;
1044# if defined(RT_OS_SOLARIS)
1045 else if (PollFd.revents & POLLIN)
1046# else
1047 else if ((PollFd.revents & (POLLIN | POLLHUP)) == (POLLIN | POLLHUP))
1048# endif
1049 {
1050 /* Check if there is actually data available. */
1051 uint8_t bPeek;
1052 ssize_t rcRecv = recv(PollFd.fd, &bPeek, 1, MSG_DONTWAIT | MSG_PEEK);
1053 if (rcRecv <= 0)
1054 fEvents = RTPOLL_EVT_ERROR;
1055 }
1056 rc = VINF_SUCCESS;
1057 }
1058 else if (rc == 0)
1059 rc = VERR_TIMEOUT;
1060 else
1061 rc = RTErrConvertFromErrno(errno);
1062 Log(("RTLocalIpcSessionWaitForData: poll returns %u (rc=%d), revents=%#x\n", cFds, rc, PollFd.revents));
1063#endif
1064
1065 int rc2 = RTCritSectEnter(&pThis->CritSect);
1066 AssertRCBreakStmt(rc2, rc = RT_SUCCESS(rc) ? rc2 : rc);
1067
1068 if (RT_SUCCESS(rc))
1069 {
1070 if (pThis->fCancelled)
1071 rc = VERR_CANCELLED;
1072 else if (fEvents & RTPOLL_EVT_ERROR)
1073 rc = VERR_BROKEN_PIPE;
1074 }
1075 else if ( rc == VERR_INTERRUPTED
1076 || rc == VERR_TRY_AGAIN)
1077 {
1078 /* Recalc cMillies. */
1079 if (cMsOriginalTimeout != RT_INDEFINITE_WAIT)
1080 {
1081 uint64_t cMsElapsed = RTTimeMilliTS() - msStart;
1082 cMillies = cMsElapsed >= cMsOriginalTimeout ? 0 : cMsOriginalTimeout - (RTMSINTERVAL)cMsElapsed;
1083 }
1084 continue;
1085 }
1086 }
1087 else
1088 rc = VERR_CANCELLED;
1089 break;
1090 }
1091
1092 pThis->hReadThread = NIL_RTTHREAD;
1093 }
1094 int rc2 = RTCritSectLeave(&pThis->CritSect);
1095 AssertStmt(RT_SUCCESS(rc2), rc = RT_SUCCESS(rc) ? rc2 : rc);
1096 }
1097
1098 rtLocalIpcSessionRelease(pThis);
1099 return rc;
1100}
1101
1102
1103/**
1104 * Get IPC session socket peer credentials.
1105 *
1106 * @returns IPRT status code.
1107 * @param hSession IPC session handle.
1108 * @param pProcess Where to return the remote peer's PID (can be NULL).
1109 * @param pUid Where to return the remote peer's UID (can be NULL).
1110 * @param pGid Where to return the remote peer's GID (can be NULL).
1111 */
1112static int rtLocalIpcSessionQueryUcred(RTLOCALIPCSESSION hSession, PRTPROCESS pProcess, PRTUID pUid, PRTGID pGid)
1113{
1114 PRTLOCALIPCSESSIONINT pThis = hSession;
1115 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1116 AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
1117
1118#if defined(RT_OS_LINUX)
1119 struct ucred PeerCred = { (pid_t)NIL_RTPROCESS, (uid_t)NIL_RTUID, (gid_t)NIL_RTGID };
1120 socklen_t cbPeerCred = sizeof(PeerCred);
1121
1122 rtLocalIpcSessionRetain(pThis);
1123
1124 int rc = RTCritSectEnter(&pThis->CritSect);;
1125 if (RT_SUCCESS(rc))
1126 {
1127 if (getsockopt(RTSocketToNative(pThis->hSocket), SOL_SOCKET, SO_PEERCRED, &PeerCred, &cbPeerCred) >= 0)
1128 {
1129 if (pProcess)
1130 *pProcess = PeerCred.pid;
1131 if (pUid)
1132 *pUid = PeerCred.uid;
1133 if (pGid)
1134 *pGid = PeerCred.gid;
1135 rc = VINF_SUCCESS;
1136 }
1137 else
1138 rc = RTErrConvertFromErrno(errno);
1139
1140 int rc2 = RTCritSectLeave(&pThis->CritSect);
1141 AssertStmt(RT_SUCCESS(rc2), rc = RT_SUCCESS(rc) ? rc2 : rc);
1142 }
1143
1144 rtLocalIpcSessionRelease(pThis);
1145
1146 return rc;
1147
1148#else
1149 /** @todo Implement on other platforms too (mostly platform specific this).
1150 * Solaris: getpeerucred? Darwin: LOCALPEERCRED or getpeereid? */
1151 RT_NOREF(pProcess, pUid, pGid);
1152 return VERR_NOT_SUPPORTED;
1153#endif
1154}
1155
1156
1157RTDECL(int) RTLocalIpcSessionQueryProcess(RTLOCALIPCSESSION hSession, PRTPROCESS pProcess)
1158{
1159 return rtLocalIpcSessionQueryUcred(hSession, pProcess, NULL, NULL);
1160}
1161
1162
1163RTDECL(int) RTLocalIpcSessionQueryUserId(RTLOCALIPCSESSION hSession, PRTUID pUid)
1164{
1165 return rtLocalIpcSessionQueryUcred(hSession, NULL, pUid, NULL);
1166}
1167
1168RTDECL(int) RTLocalIpcSessionQueryGroupId(RTLOCALIPCSESSION hSession, PRTGID pGid)
1169{
1170 return rtLocalIpcSessionQueryUcred(hSession, NULL, NULL, pGid);
1171}
1172
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use