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_"
43 sal_uInt32 m_WritePos
;
50 oslInterlockedCount m_Reference
;
53 PSECURITY_ATTRIBUTES m_Security
;
62 static oslPipe
osl_createPipeImpl(void)
66 pPipe
= static_cast< oslPipe
>(rtl_allocateZeroMemory(sizeof(struct oslPipeImpl
)));
68 pPipe
->m_bClosed
= false;
69 pPipe
->m_Reference
= 0;
70 pPipe
->m_Name
= nullptr;
71 pPipe
->m_File
= INVALID_HANDLE_VALUE
;
72 pPipe
->m_NamedObject
= nullptr;
74 pPipe
->m_ReadEvent
= CreateEventW(nullptr, TRUE
, FALSE
, nullptr);
75 pPipe
->m_WriteEvent
= CreateEventW(nullptr, TRUE
, FALSE
, nullptr);
76 pPipe
->m_AcceptEvent
= CreateEventW(nullptr, TRUE
, FALSE
, nullptr);
81 static void osl_destroyPipeImpl(oslPipe pPipe
)
85 if (pPipe
->m_NamedObject
)
86 CloseHandle(pPipe
->m_NamedObject
);
88 if (pPipe
->m_Security
)
90 free(pPipe
->m_Security
->lpSecurityDescriptor
);
91 free(pPipe
->m_Security
);
94 CloseHandle(pPipe
->m_ReadEvent
);
95 CloseHandle(pPipe
->m_WriteEvent
);
96 CloseHandle(pPipe
->m_AcceptEvent
);
99 rtl_uString_release(pPipe
->m_Name
);
105 oslPipe SAL_CALL
osl_createPipe(rtl_uString
*strPipeName
, oslPipeOptions Options
,
106 oslSecurity Security
)
108 rtl_uString
* name
= nullptr;
109 rtl_uString
* path
= nullptr;
110 rtl_uString
* temp
= nullptr;
113 PSECURITY_ATTRIBUTES pSecAttr
= nullptr;
115 rtl_uString_newFromAscii(&path
, PIPESYSTEM
);
116 rtl_uString_newFromAscii(&name
, PIPEPREFIX
);
120 rtl_uString
*Ident
= nullptr;
121 rtl_uString
*Delim
= nullptr;
123 OSL_VERIFY(osl_getUserIdent(Security
, &Ident
));
124 rtl_uString_newFromAscii(&Delim
, "_");
126 rtl_uString_newConcat(&temp
, name
, Ident
);
127 rtl_uString_newConcat(&name
, temp
, Delim
);
129 rtl_uString_release(Ident
);
130 rtl_uString_release(Delim
);
134 if (Options
& osl_Pipe_CREATE
)
136 PSECURITY_DESCRIPTOR pSecDesc
;
138 pSecDesc
= static_cast< PSECURITY_DESCRIPTOR
>(malloc(SECURITY_DESCRIPTOR_MIN_LENGTH
));
140 /* add a NULL disc. ACL to the security descriptor */
141 OSL_VERIFY(InitializeSecurityDescriptor(pSecDesc
, SECURITY_DESCRIPTOR_REVISION
));
142 OSL_VERIFY(SetSecurityDescriptorDacl(pSecDesc
, TRUE
, nullptr, FALSE
));
144 pSecAttr
= static_cast< PSECURITY_ATTRIBUTES
>(malloc(sizeof(SECURITY_ATTRIBUTES
)));
145 pSecAttr
->nLength
= sizeof(SECURITY_ATTRIBUTES
);
146 pSecAttr
->lpSecurityDescriptor
= pSecDesc
;
147 pSecAttr
->bInheritHandle
= TRUE
;
151 rtl_uString_assign(&temp
, name
);
152 rtl_uString_newConcat(&name
, temp
, strPipeName
);
155 pPipe
= osl_createPipeImpl();
157 assert(pPipe
); // if osl_createPipeImpl() cannot init. a new pipe, this is a failure
159 osl_atomic_increment(&(pPipe
->m_Reference
));
161 /* build system pipe name */
162 rtl_uString_assign(&temp
, path
);
163 rtl_uString_newConcat(&path
, temp
, name
);
164 rtl_uString_release(temp
);
167 if (Options
& osl_Pipe_CREATE
)
169 SetLastError(ERROR_SUCCESS
);
171 pPipe
->m_NamedObject
= CreateMutexW(nullptr, FALSE
, o3tl::toW(name
->buffer
));
173 if (pPipe
->m_NamedObject
)
175 if (GetLastError() != ERROR_ALREADY_EXISTS
)
177 pPipe
->m_Security
= pSecAttr
;
178 rtl_uString_assign(&pPipe
->m_Name
, name
);
180 /* try to open system pipe */
181 pPipe
->m_File
= CreateNamedPipeW(
182 o3tl::toW(path
->buffer
),
183 PIPE_ACCESS_DUPLEX
| FILE_FLAG_OVERLAPPED
,
184 PIPE_WAIT
| PIPE_TYPE_MESSAGE
| PIPE_READMODE_MESSAGE
,
185 PIPE_UNLIMITED_INSTANCES
,
187 NMPWAIT_WAIT_FOREVER
,
190 if (pPipe
->m_File
!= INVALID_HANDLE_VALUE
)
192 rtl_uString_release( name
);
193 rtl_uString_release( path
);
200 CloseHandle(pPipe
->m_NamedObject
);
201 pPipe
->m_NamedObject
= nullptr;
211 /* free instance should be available first */
212 bPipeAvailable
= WaitNamedPipeW(o3tl::toW(path
->buffer
), NMPWAIT_WAIT_FOREVER
);
214 /* first try to open system pipe */
217 pPipe
->m_File
= CreateFileW(
218 o3tl::toW(path
->buffer
),
219 GENERIC_READ
|GENERIC_WRITE
,
220 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
223 FILE_ATTRIBUTE_NORMAL
| FILE_FLAG_OVERLAPPED
,
226 if (pPipe
->m_File
!= INVALID_HANDLE_VALUE
)
229 rtl_uString_release(name
);
230 rtl_uString_release(path
);
232 // We should try to transfer our privilege to become foreground process
233 // to the other process, so that it may show popups (otherwise, they might
234 // be blocked by SPI_GETFOREGROUNDLOCKTIMEOUT setting -
235 // see SystemParametersInfo function at MSDN
236 ULONG ServerProcessId
= 0;
237 if (GetNamedPipeServerProcessId(pPipe
->m_File
, &ServerProcessId
))
238 AllowSetForegroundWindow(ServerProcessId
);
244 // Pipe instance maybe caught by another client -> try again
247 } while (bPipeAvailable
);
250 /* if we reach here something went wrong */
251 osl_destroyPipeImpl(pPipe
);
256 void SAL_CALL
osl_acquirePipe(oslPipe pPipe
)
258 osl_atomic_increment(&(pPipe
->m_Reference
));
261 void SAL_CALL
osl_releasePipe(oslPipe pPipe
)
266 if (osl_atomic_decrement(&(pPipe
->m_Reference
)) == 0)
268 if (!pPipe
->m_bClosed
)
269 osl_closePipe(pPipe
);
271 osl_destroyPipeImpl(pPipe
);
275 void SAL_CALL
osl_closePipe(oslPipe pPipe
)
277 if (pPipe
&& !pPipe
->m_bClosed
)
279 pPipe
->m_bClosed
= true;
280 /* if we have a system pipe close it */
281 if (pPipe
->m_File
!= INVALID_HANDLE_VALUE
)
283 DisconnectNamedPipe(pPipe
->m_File
);
284 CloseHandle(pPipe
->m_File
);
289 oslPipe SAL_CALL
osl_acceptPipe(oslPipe pPipe
)
291 oslPipe pAcceptedPipe
= nullptr;
295 DWORD nBytesTransfered
;
296 rtl_uString
* path
= nullptr;
297 rtl_uString
* temp
= nullptr;
299 SAL_WARN_IF(!pPipe
, "sal.osl.pipe", "osl_acceptPipe: invalid pipe");
303 SAL_WARN_IF(pPipe
->m_File
== INVALID_HANDLE_VALUE
, "sal.osl.pipe", "osl_acceptPipe: invalid handle");
305 memset(&os
, 0, sizeof(OVERLAPPED
));
306 os
.hEvent
= pPipe
->m_AcceptEvent
;
307 ResetEvent(pPipe
->m_AcceptEvent
);
309 if (!ConnectNamedPipe(pPipe
->m_File
, &os
))
311 switch (GetLastError())
313 case ERROR_PIPE_CONNECTED
: // Client already connected to pipe
314 case ERROR_NO_DATA
: // Client was connected but has already closed pipe end
315 // should only appear in nonblocking mode but in fact does
316 // in blocking asynchronous mode.
318 case ERROR_PIPE_LISTENING
: // Only for nonblocking mode but see ERROR_NO_DATA
319 case ERROR_IO_PENDING
: // This is normal if not client is connected yet
320 case ERROR_MORE_DATA
: // Should not happen
321 // blocking call to accept
322 if( !GetOverlappedResult(pPipe
->m_File
, &os
, &nBytesTransfered
, TRUE
))
324 // Possible error could be that between ConnectNamedPipe and
325 // GetOverlappedResult a connect took place.
327 switch (GetLastError())
329 case ERROR_PIPE_CONNECTED
: // Pipe was already connected
330 case ERROR_NO_DATA
: // Pipe was connected but client has already closed -> ver fast client ;-)
331 break; // Everything's fine !!!
333 // Something went wrong
338 default: // All other error say that somethings going wrong.
343 pAcceptedPipe
= osl_createPipeImpl();
344 assert(pAcceptedPipe
); // should never be the case that an oslPipe cannot be initialized
346 osl_atomic_increment(&(pAcceptedPipe
->m_Reference
));
347 rtl_uString_assign(&pAcceptedPipe
->m_Name
, pPipe
->m_Name
);
348 pAcceptedPipe
->m_File
= pPipe
->m_File
;
350 rtl_uString_newFromAscii(&temp
, PIPESYSTEM
);
351 rtl_uString_newConcat(&path
, temp
, pPipe
->m_Name
);
352 rtl_uString_release(temp
);
354 // prepare for next accept
356 CreateNamedPipeW(o3tl::toW(path
->buffer
),
357 PIPE_ACCESS_DUPLEX
| FILE_FLAG_OVERLAPPED
,
358 PIPE_WAIT
| PIPE_TYPE_MESSAGE
| PIPE_READMODE_MESSAGE
,
359 PIPE_UNLIMITED_INSTANCES
,
361 NMPWAIT_WAIT_FOREVER
,
362 pAcceptedPipe
->m_Security
);
363 rtl_uString_release(path
);
365 return pAcceptedPipe
;
368 sal_Int32 SAL_CALL
osl_receivePipe(oslPipe pPipe
,
370 sal_Int32 BytesToRead
)
377 memset(&os
, 0, sizeof(OVERLAPPED
));
378 os
.hEvent
= pPipe
->m_ReadEvent
;
380 ResetEvent(pPipe
->m_ReadEvent
);
382 if (!ReadFile(pPipe
->m_File
, pBuffer
, BytesToRead
, &nBytes
, &os
) &&
383 ((GetLastError() != ERROR_IO_PENDING
) ||
384 !GetOverlappedResult(pPipe
->m_File
, &os
, &nBytes
, TRUE
)))
386 DWORD lastError
= GetLastError();
388 if (lastError
== ERROR_MORE_DATA
)
390 nBytes
= BytesToRead
;
394 if (lastError
== ERROR_PIPE_NOT_CONNECTED
)
399 pPipe
->m_Error
= osl_Pipe_E_ConnectionAbort
;
406 sal_Int32 SAL_CALL
osl_sendPipe(oslPipe pPipe
,
408 sal_Int32 BytesToSend
)
415 memset(&os
, 0, sizeof(OVERLAPPED
));
416 os
.hEvent
= pPipe
->m_WriteEvent
;
417 ResetEvent(pPipe
->m_WriteEvent
);
419 if (!WriteFile(pPipe
->m_File
, pBuffer
, BytesToSend
, &nBytes
, &os
) &&
420 ((GetLastError() != ERROR_IO_PENDING
) ||
421 !GetOverlappedResult(pPipe
->m_File
, &os
, &nBytes
, TRUE
)))
423 if (GetLastError() == ERROR_PIPE_NOT_CONNECTED
)
428 pPipe
->m_Error
= osl_Pipe_E_ConnectionAbort
;
434 sal_Int32 SAL_CALL
osl_writePipe(oslPipe pPipe
, const void *pBuffer
, sal_Int32 n
)
436 /* loop until all desired bytes were send or an error occurred */
437 sal_Int32 BytesSend
= 0;
438 sal_Int32 BytesToSend
= n
;
440 SAL_WARN_IF(!pPipe
, "sal.osl.pipe", "osl_writePipe: invalid pipe");
441 while (BytesToSend
> 0)
445 RetVal
= osl_sendPipe(pPipe
, pBuffer
, BytesToSend
);
447 /* error occurred? */
451 BytesToSend
-= RetVal
;
453 pBuffer
= static_cast< sal_Char
const* >(pBuffer
) + RetVal
;
459 sal_Int32 SAL_CALL
osl_readPipe(oslPipe pPipe
, void *pBuffer
, sal_Int32 n
)
461 /* loop until all desired bytes were read or an error occurred */
462 sal_Int32 BytesRead
= 0;
463 sal_Int32 BytesToRead
= n
;
465 SAL_WARN_IF(!pPipe
, "sal.osl.pipe", "osl_readPipe: invalid pipe");
466 while (BytesToRead
> 0)
469 RetVal
= osl_receivePipe(pPipe
, pBuffer
, BytesToRead
);
471 /* error occurred? */
475 BytesToRead
-= RetVal
;
477 pBuffer
= static_cast< sal_Char
* >(pBuffer
) + RetVal
;
482 oslPipeError SAL_CALL
osl_getLastPipeError(oslPipe pPipe
)
488 Error
= pPipe
->m_Error
;
489 pPipe
->m_Error
= osl_Pipe_E_None
;
493 Error
= osl_Pipe_E_NotFound
;
499 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */