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 .
23 #include <osl/diagnose.h>
24 #include <osl/thread.h>
25 #include <osl/mutex.h>
26 #include <osl/conditn.h>
27 #include <osl/interlck.h>
28 #include <osl/process.h>
29 #include <rtl/alloc.h>
30 #include <sal/log.hxx>
31 #include <o3tl/char16_t2wchar_t.hxx>
36 #define PIPESYSTEM "\\\\.\\pipe\\"
37 #define PIPEPREFIX "OSL_PIPE_"
41 oslInterlockedCount m_Reference
;
44 PSECURITY_ATTRIBUTES m_Security
;
53 static oslPipe
osl_createPipeImpl(void)
57 pPipe
= static_cast< oslPipe
>(rtl_allocateZeroMemory(sizeof(struct oslPipeImpl
)));
59 pPipe
->m_bClosed
= false;
60 pPipe
->m_Reference
= 0;
61 pPipe
->m_Name
= nullptr;
62 pPipe
->m_File
= INVALID_HANDLE_VALUE
;
63 pPipe
->m_NamedObject
= nullptr;
65 pPipe
->m_ReadEvent
= CreateEventW(nullptr, TRUE
, FALSE
, nullptr);
66 pPipe
->m_WriteEvent
= CreateEventW(nullptr, TRUE
, FALSE
, nullptr);
67 pPipe
->m_AcceptEvent
= CreateEventW(nullptr, TRUE
, FALSE
, nullptr);
72 static void osl_destroyPipeImpl(oslPipe pPipe
)
77 if (pPipe
->m_NamedObject
)
78 CloseHandle(pPipe
->m_NamedObject
);
80 if (pPipe
->m_Security
)
82 free(pPipe
->m_Security
->lpSecurityDescriptor
);
83 free(pPipe
->m_Security
);
86 CloseHandle(pPipe
->m_ReadEvent
);
87 CloseHandle(pPipe
->m_WriteEvent
);
88 CloseHandle(pPipe
->m_AcceptEvent
);
91 rtl_uString_release(pPipe
->m_Name
);
96 oslPipe SAL_CALL
osl_createPipe(rtl_uString
*strPipeName
, oslPipeOptions Options
,
99 rtl_uString
* name
= nullptr;
100 rtl_uString
* path
= nullptr;
101 rtl_uString
* temp
= nullptr;
104 PSECURITY_ATTRIBUTES pSecAttr
= nullptr;
106 rtl_uString_newFromAscii(&path
, PIPESYSTEM
);
107 rtl_uString_newFromAscii(&name
, PIPEPREFIX
);
111 rtl_uString
*Ident
= nullptr;
112 rtl_uString
*Delim
= nullptr;
114 OSL_VERIFY(osl_getUserIdent(Security
, &Ident
));
115 rtl_uString_newFromAscii(&Delim
, "_");
117 rtl_uString_newConcat(&temp
, name
, Ident
);
118 rtl_uString_newConcat(&name
, temp
, Delim
);
120 rtl_uString_release(Ident
);
121 rtl_uString_release(Delim
);
125 if (Options
& osl_Pipe_CREATE
)
127 PSECURITY_DESCRIPTOR pSecDesc
;
129 pSecDesc
= static_cast< PSECURITY_DESCRIPTOR
>(malloc(SECURITY_DESCRIPTOR_MIN_LENGTH
));
130 assert(pSecDesc
); // Don't handle OOM conditions
132 /* add a NULL disc. ACL to the security descriptor */
133 OSL_VERIFY(InitializeSecurityDescriptor(pSecDesc
, SECURITY_DESCRIPTOR_REVISION
));
134 OSL_VERIFY(SetSecurityDescriptorDacl(pSecDesc
, TRUE
, nullptr, FALSE
));
136 pSecAttr
= static_cast< PSECURITY_ATTRIBUTES
>(malloc(sizeof(SECURITY_ATTRIBUTES
)));
137 assert(pSecAttr
); // Don't handle OOM conditions
138 pSecAttr
->nLength
= sizeof(SECURITY_ATTRIBUTES
);
139 pSecAttr
->lpSecurityDescriptor
= pSecDesc
;
140 pSecAttr
->bInheritHandle
= TRUE
;
144 rtl_uString_assign(&temp
, name
);
145 rtl_uString_newConcat(&name
, temp
, strPipeName
);
148 pPipe
= osl_createPipeImpl();
150 assert(pPipe
); // if osl_createPipeImpl() cannot init. a new pipe, this is a failure
152 osl_atomic_increment(&(pPipe
->m_Reference
));
154 /* build system pipe name */
155 rtl_uString_assign(&temp
, path
);
156 rtl_uString_newConcat(&path
, temp
, name
);
157 rtl_uString_release(temp
);
160 if (Options
& osl_Pipe_CREATE
)
162 SetLastError(ERROR_SUCCESS
);
164 pPipe
->m_NamedObject
= CreateMutexW(nullptr, FALSE
, o3tl::toW(name
->buffer
));
166 if (pPipe
->m_NamedObject
)
168 if (GetLastError() != ERROR_ALREADY_EXISTS
)
170 pPipe
->m_Security
= pSecAttr
;
171 rtl_uString_assign(&pPipe
->m_Name
, name
);
173 /* try to open system pipe */
174 pPipe
->m_File
= CreateNamedPipeW(
175 o3tl::toW(path
->buffer
),
176 PIPE_ACCESS_DUPLEX
| FILE_FLAG_OVERLAPPED
,
177 PIPE_WAIT
| PIPE_TYPE_MESSAGE
| PIPE_READMODE_MESSAGE
,
178 PIPE_UNLIMITED_INSTANCES
,
180 NMPWAIT_WAIT_FOREVER
,
183 if (pPipe
->m_File
!= INVALID_HANDLE_VALUE
)
185 rtl_uString_release( name
);
186 rtl_uString_release( path
);
193 CloseHandle(pPipe
->m_NamedObject
);
194 pPipe
->m_NamedObject
= nullptr;
204 /* free instance should be available first */
205 bPipeAvailable
= WaitNamedPipeW(o3tl::toW(path
->buffer
), NMPWAIT_WAIT_FOREVER
);
207 /* first try to open system pipe */
210 pPipe
->m_File
= CreateFileW(
211 o3tl::toW(path
->buffer
),
212 GENERIC_READ
|GENERIC_WRITE
,
213 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
216 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_OVERLAPPED
,
219 if (pPipe
->m_File
!= INVALID_HANDLE_VALUE
)
222 rtl_uString_release(name
);
223 rtl_uString_release(path
);
225 // We should try to transfer our privilege to become foreground process
226 // to the other process, so that it may show popups (otherwise, they might
227 // be blocked by SPI_GETFOREGROUNDLOCKTIMEOUT setting -
228 // see SystemParametersInfo function at MSDN
229 ULONG ServerProcessId
= 0;
230 if (GetNamedPipeServerProcessId(pPipe
->m_File
, &ServerProcessId
))
231 AllowSetForegroundWindow(ServerProcessId
);
237 // Pipe instance maybe caught by another client -> try again
240 } while (bPipeAvailable
);
243 /* if we reach here something went wrong */
244 osl_destroyPipeImpl(pPipe
);
249 void SAL_CALL
osl_acquirePipe(oslPipe pPipe
)
251 osl_atomic_increment(&(pPipe
->m_Reference
));
254 void SAL_CALL
osl_releasePipe(oslPipe pPipe
)
259 if (osl_atomic_decrement(&(pPipe
->m_Reference
)) == 0)
261 if (!pPipe
->m_bClosed
)
262 osl_closePipe(pPipe
);
264 osl_destroyPipeImpl(pPipe
);
268 void SAL_CALL
osl_closePipe(oslPipe pPipe
)
270 if (pPipe
&& !pPipe
->m_bClosed
)
272 pPipe
->m_bClosed
= true;
273 /* if we have a system pipe close it */
274 if (pPipe
->m_File
!= INVALID_HANDLE_VALUE
)
276 DisconnectNamedPipe(pPipe
->m_File
);
277 CloseHandle(pPipe
->m_File
);
282 oslPipe SAL_CALL
osl_acceptPipe(oslPipe pPipe
)
284 oslPipe pAcceptedPipe
= nullptr;
288 DWORD nBytesTransferred
;
289 rtl_uString
* path
= nullptr;
290 rtl_uString
* temp
= nullptr;
292 SAL_WARN_IF(!pPipe
, "sal.osl.pipe", "osl_acceptPipe: invalid pipe");
296 SAL_WARN_IF(pPipe
->m_File
== INVALID_HANDLE_VALUE
, "sal.osl.pipe", "osl_acceptPipe: invalid handle");
298 os
.hEvent
= pPipe
->m_AcceptEvent
;
299 ResetEvent(pPipe
->m_AcceptEvent
);
301 if (!ConnectNamedPipe(pPipe
->m_File
, &os
))
303 switch (GetLastError())
305 case ERROR_PIPE_CONNECTED
: // Client already connected to pipe
306 case ERROR_NO_DATA
: // Client was connected but has already closed pipe end
307 // should only appear in nonblocking mode but in fact does
308 // in blocking asynchronous mode.
310 case ERROR_PIPE_LISTENING
: // Only for nonblocking mode but see ERROR_NO_DATA
311 case ERROR_IO_PENDING
: // This is normal if not client is connected yet
312 case ERROR_MORE_DATA
: // Should not happen
313 // blocking call to accept
314 if( !GetOverlappedResult(pPipe
->m_File
, &os
, &nBytesTransferred
, TRUE
))
316 // Possible error could be that between ConnectNamedPipe and
317 // GetOverlappedResult a connect took place.
319 switch (GetLastError())
321 case ERROR_PIPE_CONNECTED
: // Pipe was already connected
322 case ERROR_NO_DATA
: // Pipe was connected but client has already closed -> ver fast client ;-)
323 break; // Everything's fine !!!
325 // Something went wrong
330 default: // All other error say that somethings going wrong.
335 pAcceptedPipe
= osl_createPipeImpl();
336 assert(pAcceptedPipe
); // should never be the case that an oslPipe cannot be initialized
338 osl_atomic_increment(&(pAcceptedPipe
->m_Reference
));
339 rtl_uString_assign(&pAcceptedPipe
->m_Name
, pPipe
->m_Name
);
340 pAcceptedPipe
->m_File
= pPipe
->m_File
;
342 rtl_uString_newFromAscii(&temp
, PIPESYSTEM
);
343 rtl_uString_newConcat(&path
, temp
, pPipe
->m_Name
);
344 rtl_uString_release(temp
);
346 // prepare for next accept
348 CreateNamedPipeW(o3tl::toW(path
->buffer
),
349 PIPE_ACCESS_DUPLEX
| FILE_FLAG_OVERLAPPED
,
350 PIPE_WAIT
| PIPE_TYPE_MESSAGE
| PIPE_READMODE_MESSAGE
,
351 PIPE_UNLIMITED_INSTANCES
,
353 NMPWAIT_WAIT_FOREVER
,
354 pAcceptedPipe
->m_Security
);
355 rtl_uString_release(path
);
357 return pAcceptedPipe
;
360 sal_Int32 SAL_CALL
osl_receivePipe(oslPipe pPipe
,
362 sal_Int32 BytesToRead
)
369 os
.hEvent
= pPipe
->m_ReadEvent
;
371 ResetEvent(pPipe
->m_ReadEvent
);
373 if (!ReadFile(pPipe
->m_File
, pBuffer
, BytesToRead
, &nBytes
, &os
) &&
374 ((GetLastError() != ERROR_IO_PENDING
) ||
375 !GetOverlappedResult(pPipe
->m_File
, &os
, &nBytes
, TRUE
)))
377 DWORD lastError
= GetLastError();
379 if (lastError
== ERROR_MORE_DATA
)
381 nBytes
= BytesToRead
;
385 if (lastError
== ERROR_PIPE_NOT_CONNECTED
)
390 pPipe
->m_Error
= osl_Pipe_E_ConnectionAbort
;
397 sal_Int32 SAL_CALL
osl_sendPipe(oslPipe pPipe
,
399 sal_Int32 BytesToSend
)
406 os
.hEvent
= pPipe
->m_WriteEvent
;
407 ResetEvent(pPipe
->m_WriteEvent
);
409 if (!WriteFile(pPipe
->m_File
, pBuffer
, BytesToSend
, &nBytes
, &os
) &&
410 ((GetLastError() != ERROR_IO_PENDING
) ||
411 !GetOverlappedResult(pPipe
->m_File
, &os
, &nBytes
, TRUE
)))
413 if (GetLastError() == ERROR_PIPE_NOT_CONNECTED
)
418 pPipe
->m_Error
= osl_Pipe_E_ConnectionAbort
;
424 sal_Int32 SAL_CALL
osl_writePipe(oslPipe pPipe
, const void *pBuffer
, sal_Int32 n
)
426 /* loop until all desired bytes were send or an error occurred */
427 sal_Int32 BytesSend
= 0;
428 sal_Int32 BytesToSend
= n
;
430 SAL_WARN_IF(!pPipe
, "sal.osl.pipe", "osl_writePipe: invalid pipe");
431 while (BytesToSend
> 0)
435 RetVal
= osl_sendPipe(pPipe
, pBuffer
, BytesToSend
);
437 /* error occurred? */
441 BytesToSend
-= RetVal
;
443 pBuffer
= static_cast< char const* >(pBuffer
) + RetVal
;
449 sal_Int32 SAL_CALL
osl_readPipe(oslPipe pPipe
, void *pBuffer
, sal_Int32 n
)
451 /* loop until all desired bytes were read or an error occurred */
452 sal_Int32 BytesRead
= 0;
453 sal_Int32 BytesToRead
= n
;
455 SAL_WARN_IF(!pPipe
, "sal.osl.pipe", "osl_readPipe: invalid pipe");
456 while (BytesToRead
> 0)
459 RetVal
= osl_receivePipe(pPipe
, pBuffer
, BytesToRead
);
461 /* error occurred? */
465 BytesToRead
-= RetVal
;
467 pBuffer
= static_cast< char* >(pBuffer
) + RetVal
;
472 oslPipeError SAL_CALL
osl_getLastPipeError(oslPipe pPipe
)
478 Error
= pPipe
->m_Error
;
479 pPipe
->m_Error
= osl_Pipe_E_None
;
483 Error
= osl_Pipe_E_NotFound
;
489 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */