VirtualBox

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

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

iprt: local ipc server: add interface to grant r/w access to a server socket for a specified group, bugref:10134.

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette