1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <o3tl/safeint.hxx>
22 #include <osl/diagnose.h>
23 #include <osl/thread.h>
24 #include <osl/interlck.h>
25 #include <rtl/string.h>
26 #include <rtl/ustring.h>
27 #include <rtl/bootstrap.hxx>
28 #include <sal/log.hxx>
30 #include "sockimpl.hxx"
31 #include "secimpl.hxx"
32 #include "unixerrnostring.hxx"
40 constexpr OString PIPEDEFAULTPATH
= "/tmp"_ostr
;
41 constexpr OString PIPEALTERNATEPATH
= "/var/tmp"_ostr
;
43 static oslPipe
osl_psz_createPipe(const char *pszPipeName
, oslPipeOptions Options
, oslSecurity Security
);
49 } const PipeError
[]= {
50 { 0, osl_Pipe_E_None
}, /* no error */
51 { EPROTOTYPE
, osl_Pipe_E_NoProtocol
}, /* Protocol wrong type for socket */
52 { ENOPROTOOPT
, osl_Pipe_E_NoProtocol
}, /* Protocol not available */
53 { EPROTONOSUPPORT
, osl_Pipe_E_NoProtocol
}, /* Protocol not supported */
54 #ifdef ESOCKTNOSUPPORT
55 { ESOCKTNOSUPPORT
, osl_Pipe_E_NoProtocol
}, /* Socket type not supported */
57 { EPFNOSUPPORT
, osl_Pipe_E_NoProtocol
}, /* Protocol family not supported */
58 { EAFNOSUPPORT
, osl_Pipe_E_NoProtocol
}, /* Address family not supported by */
60 { ENETRESET
, osl_Pipe_E_NetworkReset
}, /* Network dropped connection because */
62 { ECONNABORTED
, osl_Pipe_E_ConnectionAbort
}, /* Software caused connection abort */
63 { ECONNRESET
, osl_Pipe_E_ConnectionReset
}, /* Connection reset by peer */
64 { ENOBUFS
, osl_Pipe_E_NoBufferSpace
}, /* No buffer space available */
65 { ETIMEDOUT
, osl_Pipe_E_TimedOut
}, /* Connection timed out */
66 { ECONNREFUSED
, osl_Pipe_E_ConnectionRefused
}, /* Connection refused */
67 { -1, osl_Pipe_E_invalidError
}
70 static oslPipeError
osl_PipeErrorFromNative(int nativeType
)
74 while ((PipeError
[i
].error
!= osl_Pipe_E_invalidError
) &&
75 (PipeError
[i
].errcode
!= nativeType
))
80 return PipeError
[i
].error
;
83 static oslPipe
createPipeImpl()
85 oslPipe pPipeImpl
= new oslPipeImpl
;
87 pPipeImpl
->m_Socket
= 0;
88 pPipeImpl
->m_Name
[0] = 0;
89 pPipeImpl
->m_nRefCount
= 1;
90 pPipeImpl
->m_bClosed
= false;
91 #if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
92 pPipeImpl
->m_bIsInShutdown
= false;
93 pPipeImpl
->m_bIsAccepting
= false;
99 static void destroyPipeImpl(oslPipe pImpl
)
104 oslPipe SAL_CALL
osl_createPipe(rtl_uString
*ustrPipeName
, oslPipeOptions Options
, oslSecurity Security
)
106 oslPipe pPipe
= nullptr;
107 rtl_String
* strPipeName
= nullptr;
111 rtl_uString2String(&strPipeName
,
112 rtl_uString_getStr(ustrPipeName
),
113 rtl_uString_getLength(ustrPipeName
),
114 osl_getThreadTextEncoding(),
115 OUSTRING_TO_OSTRING_CVTFLAGS
);
116 char* pszPipeName
= rtl_string_getStr(strPipeName
);
117 pPipe
= osl_psz_createPipe(pszPipeName
, Options
, Security
);
120 rtl_string_release(strPipeName
);
128 getBootstrapSocketPath()
132 if (rtl::Bootstrap::get(u
"OSL_SOCKET_PATH"_ustr
, pValue
))
134 return OUStringToOString(pValue
, RTL_TEXTENCODING_UTF8
);
139 static oslPipe
osl_psz_createPipe(const char *pszPipeName
, oslPipeOptions Options
,
140 oslSecurity Security
)
144 struct sockaddr_un addr
;
149 if (access(PIPEDEFAULTPATH
.getStr(), W_OK
) == 0)
150 name
= PIPEDEFAULTPATH
;
151 else if (access(PIPEALTERNATEPATH
.getStr(), W_OK
) == 0)
152 name
= PIPEALTERNATEPATH
;
154 name
= getBootstrapSocketPath ();
165 OSL_VERIFY(osl_psz_getUserIdent(Security
, Ident
, sizeof(Ident
)));
167 name
+= OString::Concat("OSL_PIPE_") + Ident
+ "_" + pszPipeName
;
171 name
+= OString::Concat("OSL_PIPE_") + pszPipeName
;
174 if (o3tl::make_unsigned(name
.getLength()) >= sizeof addr
.sun_path
)
176 SAL_WARN("sal.osl.pipe", "osl_createPipe: pipe name too long");
181 pPipe
= createPipeImpl();
187 pPipe
->m_Socket
= socket(AF_UNIX
, SOCK_STREAM
, 0);
188 if (pPipe
->m_Socket
< 0)
190 SAL_WARN("sal.osl.pipe", "socket() failed: " << UnixErrnoString(errno
));
191 destroyPipeImpl(pPipe
);
195 /* set close-on-exec flag */
196 if ((Flags
= fcntl(pPipe
->m_Socket
, F_GETFD
, 0)) != -1)
199 if (fcntl(pPipe
->m_Socket
, F_SETFD
, Flags
) == -1)
201 SAL_WARN("sal.osl.pipe", "fcntl() failed: " << UnixErrnoString(errno
));
205 memset(&addr
, 0, sizeof(addr
));
207 SAL_INFO("sal.osl.pipe", "new pipe on fd " << pPipe
->m_Socket
<< " '" << name
<< "'");
209 addr
.sun_family
= AF_UNIX
;
210 // coverity[fixed_size_dest : FALSE] - safe, see check above
211 strcpy(addr
.sun_path
, name
.getStr());
213 len
= SUN_LEN(&addr
);
218 if (Options
& osl_Pipe_CREATE
)
222 /* check if there exists an orphan filesystem entry */
223 if ((stat(name
.getStr(), &status
) == 0) &&
224 (S_ISSOCK(status
.st_mode
) || S_ISFIFO(status
.st_mode
)))
226 if (connect(pPipe
->m_Socket
, reinterpret_cast< sockaddr
* >(&addr
), len
) >= 0)
228 close (pPipe
->m_Socket
);
229 destroyPipeImpl(pPipe
);
233 unlink(name
.getStr());
237 if (bind(pPipe
->m_Socket
, reinterpret_cast< sockaddr
* >(&addr
), len
) < 0)
239 SAL_WARN("sal.osl.pipe", "bind() failed: " << UnixErrnoString(errno
));
240 close(pPipe
->m_Socket
);
241 destroyPipeImpl(pPipe
);
245 /* Only give access to all if no security handle was specified, otherwise security
249 (void)chmod(name
.getStr(),S_IRWXU
| S_IRWXG
|S_IRWXO
);
251 strcpy(pPipe
->m_Name
, name
.getStr()); // safe, see check above
253 if (listen(pPipe
->m_Socket
, 5) < 0)
255 SAL_WARN("sal.osl.pipe", "listen() failed: " << UnixErrnoString(errno
));
256 // cid#1255391 warns about unlink(name) after stat(name, &status)
257 // above, but the intervening call to bind makes those two clearly
258 // unrelated, as it would fail if name existed at that point in
260 // coverity[toctou] - this is bogus
261 unlink(name
.getStr()); /* remove filesystem entry */
262 close(pPipe
->m_Socket
);
263 destroyPipeImpl(pPipe
);
271 if (access(name
.getStr(), F_OK
) != -1)
273 if (connect(pPipe
->m_Socket
, reinterpret_cast< sockaddr
* >(&addr
), len
) >= 0)
276 SAL_WARN("sal.osl.pipe", "connect() failed: " << UnixErrnoString(errno
));
279 close (pPipe
->m_Socket
);
280 destroyPipeImpl(pPipe
);
284 void SAL_CALL
osl_acquirePipe(oslPipe pPipe
)
286 osl_atomic_increment(&(pPipe
->m_nRefCount
));
289 void SAL_CALL
osl_releasePipe(oslPipe pPipe
)
294 if (osl_atomic_decrement(&(pPipe
->m_nRefCount
)) == 0)
296 osl_closePipe(pPipe
);
298 destroyPipeImpl(pPipe
);
302 void SAL_CALL
osl_closePipe(oslPipe pPipe
)
310 std::unique_lock
aGuard(pPipe
->m_Mutex
);
312 if (pPipe
->m_bClosed
)
315 ConnFD
= pPipe
->m_Socket
;
317 /* Thread does not return from accept on linux, so
318 connect to the accepting pipe
320 #if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
321 struct sockaddr_un addr
;
323 if (pPipe
->m_bIsAccepting
)
325 pPipe
->m_bIsInShutdown
= true;
326 pPipe
->m_Socket
= -1;
328 int fd
= socket(AF_UNIX
, SOCK_STREAM
, 0);
331 SAL_WARN("sal.osl.pipe", "socket() failed: " << UnixErrnoString(errno
));
335 memset(&addr
, 0, sizeof(addr
));
337 SAL_INFO("sal.osl.pipe", "osl_destroyPipe : Pipe Name '" << pPipe
->m_Name
<< "'");
339 addr
.sun_family
= AF_UNIX
;
340 strcpy(addr
.sun_path
, pPipe
->m_Name
); // safe, as both are same size
342 nRet
= connect(fd
, reinterpret_cast< sockaddr
* >(&addr
), sizeof(addr
));
344 SAL_WARN("sal.osl.pipe", "connect() failed: " << UnixErrnoString(errno
));
348 #endif /* CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT */
350 nRet
= shutdown(ConnFD
, 2);
352 SAL_WARN("sal.osl.pipe", "shutdown() failed: " << UnixErrnoString(errno
));
354 nRet
= close(ConnFD
);
356 SAL_WARN("sal.osl.pipe", "close() failed: " << UnixErrnoString(errno
));
358 /* remove filesystem entry */
359 if (pPipe
->m_Name
[0] != '\0')
360 unlink(pPipe
->m_Name
);
362 pPipe
->m_bClosed
= true;
365 oslPipe SAL_CALL
osl_acceptPipe(oslPipe pPipe
)
368 oslPipe pAcceptedPipe
;
370 SAL_WARN_IF(!pPipe
, "sal.osl.pipe", "invalid pipe");
376 // don't hold lock while accepting, so it is possible to close a socket blocked in accept
377 std::unique_lock
aGuard(pPipe
->m_Mutex
);
379 assert(pPipe
->m_Name
[0] != '\0'); // you cannot have an empty pipe name
380 #if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
381 pPipe
->m_bIsAccepting
= true;
384 socket
= pPipe
->m_Socket
;
388 s
= accept(socket
, nullptr, nullptr);
390 std::unique_lock
aGuard(pPipe
->m_Mutex
);
392 #if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
393 pPipe
->m_bIsAccepting
= false;
398 SAL_WARN("sal.osl.pipe", "accept() failed: " << UnixErrnoString(errno
));
402 #if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
403 if (pPipe
->m_bIsInShutdown
)
408 #endif /* CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT */
411 pAcceptedPipe
= createPipeImpl();
413 assert(pAcceptedPipe
); // should never be the case that an oslPipe cannot be initialized
420 /* set close-on-exec flag */
422 if ((flags
= fcntl(s
, F_GETFD
, 0)) >= 0)
425 if (fcntl(s
, F_SETFD
, flags
) < 0)
426 SAL_WARN("sal.osl.pipe", "fcntl() failed: " << UnixErrnoString(errno
));
429 pAcceptedPipe
->m_Socket
= s
;
431 return pAcceptedPipe
;
434 sal_Int32 SAL_CALL
osl_receivePipe(oslPipe pPipe
,
436 sal_Int32 BytesToRead
)
438 SAL_WARN_IF(!pPipe
, "sal.osl.pipe", "osl_receivePipe: invalid pipe");
441 SAL_WARN("sal.osl.pipe", "osl_receivePipe: Invalid socket");
448 // don't hold lock while receiving, so it is possible to close a socket blocked in recv
449 std::unique_lock
aGuard(pPipe
->m_Mutex
);
450 socket
= pPipe
->m_Socket
;
453 sal_Int32 nRet
= recv(socket
, pBuffer
, BytesToRead
, 0);
455 SAL_WARN_IF(nRet
< 0, "sal.osl.pipe", "recv() failed: " << UnixErrnoString(errno
));
460 sal_Int32 SAL_CALL
osl_sendPipe(oslPipe pPipe
,
462 sal_Int32 BytesToSend
)
466 SAL_WARN_IF(!pPipe
, "sal.osl.pipe", "osl_sendPipe: invalid pipe");
469 SAL_WARN("sal.osl.pipe", "osl_sendPipe: Invalid socket");
476 // don't hold lock while sending, so it is possible to close a socket blocked in send
477 std::unique_lock
aGuard(pPipe
->m_Mutex
);
478 socket
= pPipe
->m_Socket
;
481 nRet
= send(socket
, pBuffer
, BytesToSend
, 0);
484 SAL_WARN("sal.osl.pipe", "send() failed: " << UnixErrnoString(errno
));
489 oslPipeError SAL_CALL
osl_getLastPipeError(SAL_UNUSED_PARAMETER oslPipe
)
491 return osl_PipeErrorFromNative(errno
);
494 sal_Int32 SAL_CALL
osl_writePipe(oslPipe pPipe
, const void *pBuffer
, sal_Int32 n
)
496 /* loop until all desired bytes were send or an error occurred */
497 sal_Int32 BytesSend
= 0;
498 sal_Int32 BytesToSend
= n
;
500 SAL_WARN_IF(!pPipe
, "sal.osl.pipe", "osl_writePipe: invalid pipe"); // osl_sendPipe detects invalid pipe
501 while (BytesToSend
> 0)
503 // coverity[ tainted_data_return : FALSE ] version 2023.12.2
504 sal_Int32 RetVal
= osl_sendPipe(pPipe
, pBuffer
, BytesToSend
);
505 /* error occurred? */
509 BytesToSend
-= RetVal
;
511 pBuffer
= static_cast< char const* >(pBuffer
) + RetVal
;
517 sal_Int32 SAL_CALL
osl_readPipe( oslPipe pPipe
, void *pBuffer
, sal_Int32 n
)
519 /* loop until all desired bytes were read or an error occurred */
520 sal_Int32 BytesRead
= 0;
521 sal_Int32 BytesToRead
= n
;
523 SAL_WARN_IF(!pPipe
, "sal.osl.pipe", "osl_readPipe: invalid pipe"); // osl_receivePipe detects invalid pipe
524 while (BytesToRead
> 0)
526 // coverity[ tainted_data_return : FALSE ] version 2023.12.2
527 sal_Int32 RetVal
= osl_receivePipe(pPipe
, pBuffer
, BytesToRead
);
528 /* error occurred? */
532 BytesToRead
-= RetVal
;
534 pBuffer
= static_cast< char* >(pBuffer
) + RetVal
;
540 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */