stronger typing for SwClient::GetRegisteredIn" and fix SwIterator cast
[LibreOffice.git] / sal / osl / unx / pipe.cxx
blobc9c8cc31c7055c88b197e3d13d413502e5cb8d43
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
21 #include <osl/pipe.h>
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"
34 #include <cassert>
35 #include <cstring>
36 #include <fcntl.h>
37 #include <sys/stat.h>
38 #include <unistd.h>
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);
45 struct
47 int errcode;
48 oslPipeError error;
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 */
56 #endif
57 { EPFNOSUPPORT, osl_Pipe_E_NoProtocol }, /* Protocol family not supported */
58 { EAFNOSUPPORT, osl_Pipe_E_NoProtocol }, /* Address family not supported by */
59 /* protocol family */
60 { ENETRESET, osl_Pipe_E_NetworkReset }, /* Network dropped connection because */
61 /* of reset */
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)
72 int i = 0;
74 while ((PipeError[i].error != osl_Pipe_E_invalidError) &&
75 (PipeError[i].errcode != nativeType))
77 i++;
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;
94 #endif
96 return pPipeImpl;
99 static void destroyPipeImpl(oslPipe pImpl)
101 delete pImpl;
104 oslPipe SAL_CALL osl_createPipe(rtl_uString *ustrPipeName, oslPipeOptions Options, oslSecurity Security)
106 oslPipe pPipe = nullptr;
107 rtl_String* strPipeName = nullptr;
109 if (ustrPipeName)
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);
119 if (strPipeName)
120 rtl_string_release(strPipeName);
123 return pPipe;
127 static OString
128 getBootstrapSocketPath()
130 OUString pValue;
132 if (rtl::Bootstrap::get(u"OSL_SOCKET_PATH"_ustr, pValue))
134 return OUStringToOString(pValue, RTL_TEXTENCODING_UTF8);
136 return ""_ostr;
139 static oslPipe osl_psz_createPipe(const char *pszPipeName, oslPipeOptions Options,
140 oslSecurity Security)
142 int Flags;
143 size_t len;
144 struct sockaddr_un addr;
146 OString name;
147 oslPipe pPipe;
149 if (access(PIPEDEFAULTPATH.getStr(), W_OK) == 0)
150 name = PIPEDEFAULTPATH;
151 else if (access(PIPEALTERNATEPATH.getStr(), W_OK) == 0)
152 name = PIPEALTERNATEPATH;
153 else {
154 name = getBootstrapSocketPath ();
157 name += "/";
159 if (Security)
161 char Ident[256];
163 Ident[0] = '\0';
165 OSL_VERIFY(osl_psz_getUserIdent(Security, Ident, sizeof(Ident)));
167 name += OString::Concat("OSL_PIPE_") + Ident + "_" + pszPipeName;
169 else
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");
177 return nullptr;
180 /* alloc memory */
181 pPipe = createPipeImpl();
183 if (!pPipe)
184 return nullptr;
186 /* create socket */
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);
192 return nullptr;
195 /* set close-on-exec flag */
196 if ((Flags = fcntl(pPipe->m_Socket, F_GETFD, 0)) != -1)
198 Flags |= FD_CLOEXEC;
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());
212 #if defined(FREEBSD)
213 len = SUN_LEN(&addr);
214 #else
215 len = sizeof(addr);
216 #endif
218 if (Options & osl_Pipe_CREATE)
220 struct stat status;
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);
230 return nullptr;
233 unlink(name.getStr());
236 /* ok, fs clean */
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);
242 return nullptr;
245 /* Only give access to all if no security handle was specified, otherwise security
246 depends on umask */
248 if (!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
259 // time:
260 // coverity[toctou] - this is bogus
261 unlink(name.getStr()); /* remove filesystem entry */
262 close(pPipe->m_Socket);
263 destroyPipeImpl(pPipe);
264 return nullptr;
267 return pPipe;
270 /* osl_pipe_OPEN */
271 if (access(name.getStr(), F_OK) != -1)
273 if (connect(pPipe->m_Socket, reinterpret_cast< sockaddr* >(&addr), len) >= 0)
274 return pPipe;
276 SAL_WARN("sal.osl.pipe", "connect() failed: " << UnixErrnoString(errno));
279 close (pPipe->m_Socket);
280 destroyPipeImpl(pPipe);
281 return nullptr;
284 void SAL_CALL osl_acquirePipe(oslPipe pPipe)
286 osl_atomic_increment(&(pPipe->m_nRefCount));
289 void SAL_CALL osl_releasePipe(oslPipe pPipe)
291 if (!pPipe)
292 return;
294 if (osl_atomic_decrement(&(pPipe->m_nRefCount)) == 0)
296 osl_closePipe(pPipe);
298 destroyPipeImpl(pPipe);
302 void SAL_CALL osl_closePipe(oslPipe pPipe)
304 int nRet;
305 int ConnFD;
307 if (!pPipe)
308 return;
310 std::unique_lock aGuard(pPipe->m_Mutex);
312 if (pPipe->m_bClosed)
313 return;
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);
329 if (fd < 0)
331 SAL_WARN("sal.osl.pipe", "socket() failed: " << UnixErrnoString(errno));
332 return;
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));
343 if (nRet < 0)
344 SAL_WARN("sal.osl.pipe", "connect() failed: " << UnixErrnoString(errno));
346 close(fd);
348 #endif /* CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT */
350 nRet = shutdown(ConnFD, 2);
351 if (nRet < 0)
352 SAL_WARN("sal.osl.pipe", "shutdown() failed: " << UnixErrnoString(errno));
354 nRet = close(ConnFD);
355 if (nRet < 0)
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)
367 int s;
368 oslPipe pAcceptedPipe;
370 SAL_WARN_IF(!pPipe, "sal.osl.pipe", "invalid pipe");
371 if (!pPipe)
372 return nullptr;
374 int socket;
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;
382 #endif
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;
394 #endif
396 if (s < 0)
398 SAL_WARN("sal.osl.pipe", "accept() failed: " << UnixErrnoString(errno));
399 return nullptr;
402 #if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
403 if (pPipe->m_bIsInShutdown)
405 close(s);
406 return nullptr;
408 #endif /* CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT */
410 /* alloc memory */
411 pAcceptedPipe = createPipeImpl();
413 assert(pAcceptedPipe); // should never be the case that an oslPipe cannot be initialized
414 if (!pAcceptedPipe)
416 close(s);
417 return nullptr;
420 /* set close-on-exec flag */
421 int flags;
422 if ((flags = fcntl(s, F_GETFD, 0)) >= 0)
424 flags |= FD_CLOEXEC;
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,
435 void* pBuffer,
436 sal_Int32 BytesToRead)
438 SAL_WARN_IF(!pPipe, "sal.osl.pipe", "osl_receivePipe: invalid pipe");
439 if (!pPipe)
441 SAL_WARN("sal.osl.pipe", "osl_receivePipe: Invalid socket");
442 errno=EINVAL;
443 return -1;
446 int 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));
457 return nRet;
460 sal_Int32 SAL_CALL osl_sendPipe(oslPipe pPipe,
461 const void* pBuffer,
462 sal_Int32 BytesToSend)
464 int nRet=0;
466 SAL_WARN_IF(!pPipe, "sal.osl.pipe", "osl_sendPipe: invalid pipe");
467 if (!pPipe)
469 SAL_WARN("sal.osl.pipe", "osl_sendPipe: Invalid socket");
470 errno=EINVAL;
471 return -1;
474 int 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);
483 if (nRet <= 0)
484 SAL_WARN("sal.osl.pipe", "send() failed: " << UnixErrnoString(errno));
486 return nRet;
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? */
506 if (RetVal <= 0)
507 break;
509 BytesToSend -= RetVal;
510 BytesSend += RetVal;
511 pBuffer= static_cast< char const* >(pBuffer) + RetVal;
514 return BytesSend;
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? */
529 if (RetVal <= 0)
530 break;
532 BytesToRead -= RetVal;
533 BytesRead += RetVal;
534 pBuffer= static_cast< char* >(pBuffer) + RetVal;
537 return BytesRead;
540 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */