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 .
22 #include <o3tl/safeint.hxx>
24 #include <osl/diagnose.h>
25 #include <osl/thread.h>
26 #include <osl/interlck.h>
27 #include <rtl/string.h>
28 #include <rtl/ustring.h>
29 #include <rtl/bootstrap.hxx>
30 #include <sal/log.hxx>
32 #include "sockimpl.hxx"
33 #include "secimpl.hxx"
34 #include "unixerrnostring.hxx"
39 constexpr OStringLiteral PIPEDEFAULTPATH
= "/tmp";
40 constexpr OStringLiteral PIPEALTERNATEPATH
= "/var/tmp";
42 static oslPipe
osl_psz_createPipe(const char *pszPipeName
, oslPipeOptions Options
, oslSecurity Security
);
48 } const PipeError
[]= {
49 { 0, osl_Pipe_E_None
}, /* no error */
50 { EPROTOTYPE
, osl_Pipe_E_NoProtocol
}, /* Protocol wrong type for socket */
51 { ENOPROTOOPT
, osl_Pipe_E_NoProtocol
}, /* Protocol not available */
52 { EPROTONOSUPPORT
, osl_Pipe_E_NoProtocol
}, /* Protocol not supported */
53 #ifdef ESOCKTNOSUPPORT
54 { ESOCKTNOSUPPORT
, osl_Pipe_E_NoProtocol
}, /* Socket type not supported */
56 { EPFNOSUPPORT
, osl_Pipe_E_NoProtocol
}, /* Protocol family not supported */
57 { EAFNOSUPPORT
, osl_Pipe_E_NoProtocol
}, /* Address family not supported by */
59 { ENETRESET
, osl_Pipe_E_NetworkReset
}, /* Network dropped connection because */
61 { ECONNABORTED
, osl_Pipe_E_ConnectionAbort
}, /* Software caused connection abort */
62 { ECONNRESET
, osl_Pipe_E_ConnectionReset
}, /* Connection reset by peer */
63 { ENOBUFS
, osl_Pipe_E_NoBufferSpace
}, /* No buffer space available */
64 { ETIMEDOUT
, osl_Pipe_E_TimedOut
}, /* Connection timed out */
65 { ECONNREFUSED
, osl_Pipe_E_ConnectionRefused
}, /* Connection refused */
66 { -1, osl_Pipe_E_invalidError
}
69 static oslPipeError
osl_PipeErrorFromNative(int nativeType
)
73 while ((PipeError
[i
].error
!= osl_Pipe_E_invalidError
) &&
74 (PipeError
[i
].errcode
!= nativeType
))
79 return PipeError
[i
].error
;
82 static oslPipe
createPipeImpl()
86 pPipeImpl
= static_cast< oslPipe
>(calloc(1, sizeof(struct oslPipeImpl
)));
90 pPipeImpl
->m_nRefCount
= 1;
91 pPipeImpl
->m_bClosed
= false;
92 #if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
93 pPipeImpl
->m_bIsInShutdown
= false;
94 pPipeImpl
->m_bIsAccepting
= false;
100 static void destroyPipeImpl(oslPipe pImpl
)
106 oslPipe SAL_CALL
osl_createPipe(rtl_uString
*ustrPipeName
, oslPipeOptions Options
, oslSecurity Security
)
108 oslPipe pPipe
= nullptr;
109 rtl_String
* strPipeName
= nullptr;
113 rtl_uString2String(&strPipeName
,
114 rtl_uString_getStr(ustrPipeName
),
115 rtl_uString_getLength(ustrPipeName
),
116 osl_getThreadTextEncoding(),
117 OUSTRING_TO_OSTRING_CVTFLAGS
);
118 char* pszPipeName
= rtl_string_getStr(strPipeName
);
119 pPipe
= osl_psz_createPipe(pszPipeName
, Options
, Security
);
122 rtl_string_release(strPipeName
);
130 getBootstrapSocketPath()
134 if (rtl::Bootstrap::get("OSL_SOCKET_PATH", pValue
))
136 return OUStringToOString(pValue
, RTL_TEXTENCODING_UTF8
);
141 static oslPipe
osl_psz_createPipe(const char *pszPipeName
, oslPipeOptions Options
,
142 oslSecurity Security
)
146 struct sockaddr_un addr
;
151 if (access(PIPEDEFAULTPATH
.getStr(), W_OK
) == 0)
152 name
= PIPEDEFAULTPATH
;
153 else if (access(PIPEALTERNATEPATH
.getStr(), W_OK
) == 0)
154 name
= PIPEALTERNATEPATH
;
156 name
= getBootstrapSocketPath ();
167 OSL_VERIFY(osl_psz_getUserIdent(Security
, Ident
, sizeof(Ident
)));
169 name
+= OString::Concat("OSL_PIPE_") + Ident
+ "_" + pszPipeName
;
173 name
+= OString::Concat("OSL_PIPE_") + pszPipeName
;
176 if (o3tl::make_unsigned(name
.getLength()) >= sizeof addr
.sun_path
)
178 SAL_WARN("sal.osl.pipe", "osl_createPipe: pipe name too long");
183 pPipe
= createPipeImpl();
189 pPipe
->m_Socket
= socket(AF_UNIX
, SOCK_STREAM
, 0);
190 if (pPipe
->m_Socket
< 0)
192 SAL_WARN("sal.osl.pipe", "socket() failed: " << UnixErrnoString(errno
));
193 destroyPipeImpl(pPipe
);
197 /* set close-on-exec flag */
198 if ((Flags
= fcntl(pPipe
->m_Socket
, F_GETFD
, 0)) != -1)
201 if (fcntl(pPipe
->m_Socket
, F_SETFD
, Flags
) == -1)
203 SAL_WARN("sal.osl.pipe", "fcntl() failed: " << UnixErrnoString(errno
));
207 memset(&addr
, 0, sizeof(addr
));
209 SAL_INFO("sal.osl.pipe", "new pipe on fd " << pPipe
->m_Socket
<< " '" << name
<< "'");
211 addr
.sun_family
= AF_UNIX
;
212 // coverity[fixed_size_dest : FALSE] - safe, see check above
213 strcpy(addr
.sun_path
, name
.getStr());
215 len
= SUN_LEN(&addr
);
220 if (Options
& osl_Pipe_CREATE
)
224 /* check if there exists an orphan filesystem entry */
225 if ((stat(name
.getStr(), &status
) == 0) &&
226 (S_ISSOCK(status
.st_mode
) || S_ISFIFO(status
.st_mode
)))
228 if (connect(pPipe
->m_Socket
, reinterpret_cast< sockaddr
* >(&addr
), len
) >= 0)
230 close (pPipe
->m_Socket
);
231 destroyPipeImpl(pPipe
);
235 unlink(name
.getStr());
239 if (bind(pPipe
->m_Socket
, reinterpret_cast< sockaddr
* >(&addr
), len
) < 0)
241 SAL_WARN("sal.osl.pipe", "bind() failed: " << UnixErrnoString(errno
));
242 close(pPipe
->m_Socket
);
243 destroyPipeImpl(pPipe
);
247 /* Only give access to all if no security handle was specified, otherwise security
251 (void)chmod(name
.getStr(),S_IRWXU
| S_IRWXG
|S_IRWXO
);
253 strcpy(pPipe
->m_Name
, name
.getStr()); // safe, see check above
255 if (listen(pPipe
->m_Socket
, 5) < 0)
257 SAL_WARN("sal.osl.pipe", "listen() failed: " << UnixErrnoString(errno
));
258 // cid#1255391 warns about unlink(name) after stat(name, &status)
259 // above, but the intervening call to bind makes those two clearly
260 // unrelated, as it would fail if name existed at that point in
262 // coverity[toctou] - this is bogus
263 unlink(name
.getStr()); /* remove filesystem entry */
264 close(pPipe
->m_Socket
);
265 destroyPipeImpl(pPipe
);
273 if (access(name
.getStr(), F_OK
) != -1)
275 if (connect(pPipe
->m_Socket
, reinterpret_cast< sockaddr
* >(&addr
), len
) >= 0)
278 SAL_WARN("sal.osl.pipe", "connect() failed: " << UnixErrnoString(errno
));
281 close (pPipe
->m_Socket
);
282 destroyPipeImpl(pPipe
);
286 void SAL_CALL
osl_acquirePipe(oslPipe pPipe
)
288 osl_atomic_increment(&(pPipe
->m_nRefCount
));
291 void SAL_CALL
osl_releasePipe(oslPipe pPipe
)
296 if (osl_atomic_decrement(&(pPipe
->m_nRefCount
)) == 0)
298 if (!pPipe
->m_bClosed
)
299 osl_closePipe(pPipe
);
301 destroyPipeImpl(pPipe
);
305 void SAL_CALL
osl_closePipe(oslPipe pPipe
)
313 if (pPipe
->m_bClosed
)
316 ConnFD
= pPipe
->m_Socket
;
318 /* Thread does not return from accept on linux, so
319 connect to the accepting pipe
321 #if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
322 struct sockaddr_un addr
;
324 if (pPipe
->m_bIsAccepting
)
326 pPipe
->m_bIsInShutdown
= true;
327 pPipe
->m_Socket
= -1;
329 int fd
= socket(AF_UNIX
, SOCK_STREAM
, 0);
332 SAL_WARN("sal.osl.pipe", "socket() failed: " << UnixErrnoString(errno
));
336 memset(&addr
, 0, sizeof(addr
));
338 SAL_INFO("sal.osl.pipe", "osl_destroyPipe : Pipe Name '" << pPipe
->m_Name
<< "'");
340 addr
.sun_family
= AF_UNIX
;
341 strcpy(addr
.sun_path
, pPipe
->m_Name
); // safe, as both are same size
343 nRet
= connect(fd
, reinterpret_cast< sockaddr
* >(&addr
), sizeof(addr
));
345 SAL_WARN("sal.osl.pipe", "connect() failed: " << UnixErrnoString(errno
));
349 #endif /* CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT */
351 nRet
= shutdown(ConnFD
, 2);
353 SAL_WARN("sal.osl.pipe", "shutdown() failed: " << UnixErrnoString(errno
));
355 nRet
= close(ConnFD
);
357 SAL_WARN("sal.osl.pipe", "close() failed: " << UnixErrnoString(errno
));
359 /* remove filesystem entry */
360 if (pPipe
->m_Name
[0] != '\0')
361 unlink(pPipe
->m_Name
);
363 pPipe
->m_bClosed
= true;
366 oslPipe SAL_CALL
osl_acceptPipe(oslPipe pPipe
)
369 oslPipe pAcceptedPipe
;
371 SAL_WARN_IF(!pPipe
, "sal.osl.pipe", "invalid pipe");
375 assert(pPipe
->m_Name
[0] != '\0'); // you cannot have an empty pipe name
377 #if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
378 pPipe
->m_bIsAccepting
= true;
381 s
= accept(pPipe
->m_Socket
, nullptr, nullptr);
383 #if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
384 pPipe
->m_bIsAccepting
= false;
389 SAL_WARN("sal.osl.pipe", "accept() failed: " << UnixErrnoString(errno
));
393 #if defined(CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT)
394 if (pPipe
->m_bIsInShutdown
)
399 #endif /* CLOSESOCKET_DOESNT_WAKE_UP_ACCEPT */
402 pAcceptedPipe
= createPipeImpl();
404 assert(pAcceptedPipe
); // should never be the case that an oslPipe cannot be initialized
411 /* set close-on-exec flag */
413 if ((flags
= fcntl(s
, F_GETFD
, 0)) >= 0)
416 if (fcntl(s
, F_SETFD
, flags
) < 0)
417 SAL_WARN("sal.osl.pipe", "fcntl() failed: " << UnixErrnoString(errno
));
420 pAcceptedPipe
->m_Socket
= s
;
422 return pAcceptedPipe
;
425 sal_Int32 SAL_CALL
osl_receivePipe(oslPipe pPipe
,
427 sal_Int32 BytesToRead
)
431 SAL_WARN_IF(!pPipe
, "sal.osl.pipe", "osl_receivePipe: invalid pipe");
434 SAL_WARN("sal.osl.pipe", "osl_receivePipe: Invalid socket");
439 nRet
= recv(pPipe
->m_Socket
, pBuffer
, BytesToRead
, 0);
442 SAL_WARN("sal.osl.pipe", "recv() failed: " << UnixErrnoString(errno
));
447 sal_Int32 SAL_CALL
osl_sendPipe(oslPipe pPipe
,
449 sal_Int32 BytesToSend
)
453 SAL_WARN_IF(!pPipe
, "sal.osl.pipe", "osl_sendPipe: invalid pipe");
456 SAL_WARN("sal.osl.pipe", "osl_sendPipe: Invalid socket");
461 nRet
= send(pPipe
->m_Socket
, pBuffer
, BytesToSend
, 0);
464 SAL_WARN("sal.osl.pipe", "send() failed: " << UnixErrnoString(errno
));
469 oslPipeError SAL_CALL
osl_getLastPipeError(SAL_UNUSED_PARAMETER oslPipe
)
471 return osl_PipeErrorFromNative(errno
);
474 sal_Int32 SAL_CALL
osl_writePipe(oslPipe pPipe
, const void *pBuffer
, sal_Int32 n
)
476 /* loop until all desired bytes were send or an error occurred */
477 sal_Int32 BytesSend
= 0;
478 sal_Int32 BytesToSend
= n
;
480 SAL_WARN_IF(!pPipe
, "sal.osl.pipe", "osl_writePipe: invalid pipe"); // osl_sendPipe detects invalid pipe
481 while (BytesToSend
> 0)
485 RetVal
= osl_sendPipe(pPipe
, pBuffer
, BytesToSend
);
487 /* error occurred? */
491 BytesToSend
-= RetVal
;
493 pBuffer
= static_cast< char const* >(pBuffer
) + RetVal
;
499 sal_Int32 SAL_CALL
osl_readPipe( oslPipe pPipe
, void *pBuffer
, sal_Int32 n
)
501 /* loop until all desired bytes were read or an error occurred */
502 sal_Int32 BytesRead
= 0;
503 sal_Int32 BytesToRead
= n
;
505 SAL_WARN_IF(!pPipe
, "sal.osl.pipe", "osl_readPipe: invalid pipe"); // osl_receivePipe detects invalid pipe
506 while (BytesToRead
> 0)
509 RetVal
= osl_receivePipe(pPipe
, pBuffer
, BytesToRead
);
511 /* error occurred? */
515 BytesToRead
-= RetVal
;
517 pBuffer
= static_cast< char* >(pBuffer
) + RetVal
;
523 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */