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 .
28 #include <osl/nlsupport.h>
29 #include <o3tl/char16_t2wchar_t.hxx>
31 #include "filetime.hxx"
32 #include "nlsupport.hxx"
33 #include "procimpl.hxx"
34 #include "file_url.hxx"
35 #include "path_helper.hxx"
36 #include <rtl/alloc.h>
37 #include <sal/log.hxx>
39 oslProcessError SAL_CALL
osl_terminateProcess(oslProcess Process
)
41 if (Process
== nullptr)
42 return osl_Process_E_Unknown
;
44 HANDLE hProcess
= static_cast<oslProcessImpl
*>(Process
)->m_hProcess
;
45 DWORD dwPID
= GetProcessId(hProcess
);
47 // cannot be System Process (0x00000000)
49 return osl_Process_E_InvalidError
;
51 // Test to see if we can create a thread in a process... adapted from:
52 // * https://support.microsoft.com/en-us/help/178893/how-to-terminate-an-application-cleanly-in-win32
53 // * http://www.drdobbs.com/a-safer-alternative-to-terminateprocess/184416547
55 // TODO: we really should firstly check to see if we have access to create threads and only
56 // duplicate the handle with elevated access if we don't have access... this can be done, but
57 // it's not exactly easy - an example can be found here:
58 // http://windowsitpro.com/site-files/windowsitpro.com/files/archive/windowsitpro.com/content/content/15989/listing_01.txt
60 HANDLE hDupProcess
= nullptr;
63 // we need to make sure we can create a thread in the remote process, if the handle was created
64 // in something that doesn't give us appropriate levels of access then we will need to give it the
65 // desired level of access - if the process handle was grabbed from OpenProcess it's quite possible
66 // that the handle doesn't have the appropriate level of access...
68 // see https://msdn.microsoft.com/en-au/library/windows/desktop/ms684880(v=vs.85).aspx
69 DWORD
const dwAccessFlags
= (PROCESS_CREATE_THREAD
| PROCESS_QUERY_INFORMATION
| PROCESS_VM_OPERATION
70 | PROCESS_VM_WRITE
| PROCESS_VM_READ
);
72 BOOL bHaveDuplHdl
= DuplicateHandle(GetCurrentProcess(), // handle to process that has handle
73 hProcess
, // handle to be duplicated
74 GetCurrentProcess(), // process that will get the dup handle
75 &hDupProcess
, // store duplicate process handle here
76 dwAccessFlags
, // desired access
77 FALSE
, // handle can't be inherited
78 0); // zero means no additional action needed
81 hProcess
= hDupProcess
; // so we were able to duplicate the handle, all good...
83 SAL_WARN("sal.osl", "Could not duplicate process handle, let's hope for the best...");
85 DWORD dwProcessStatus
= 0;
86 HANDLE hRemoteThread
= nullptr;
88 if (GetExitCodeProcess(hProcess
, &dwProcessStatus
) && (dwProcessStatus
== STILL_ACTIVE
))
90 // We need to get the address of the Win32 procedure ExitProcess, can't call it
91 // directly because we'll be calling the thunk and that will probably lead to an
92 // access violation. Once we have the address, then we need to create a new
93 // thread in the process (which we might need to run in the address space of
94 // another process) and then call on ExitProcess to try to cleanly terminate that
97 DWORD dwTID
= 0; // dummy variable as we don't need to track the thread ID
99 // Note: we want to call on ExitProcess() and not TerminateProcess() - this is
100 // because with ExitProcess() Windows notifies all attached dlls that the process
101 // is detaching from the dll, but TerminateProcess() terminates all threads
102 // immediately, doesn't call any termination handlers and doesn't notify any dlls
103 // that it is detaching from them
105 HINSTANCE hKernel
= GetModuleHandleW(L
"kernel32.dll");
106 FARPROC pfnExitProc
= GetProcAddress(hKernel
, "ExitProcess");
107 hRemoteThread
= CreateRemoteThread(
108 hProcess
, /* process handle */
109 nullptr, /* default security descriptor */
110 0, /* initial size of stack in bytes is default
111 size for executable */
112 reinterpret_cast<LPTHREAD_START_ROUTINE
>(pfnExitProc
), /* Win32 ExitProcess() */
113 reinterpret_cast<PVOID
>(UINT(0)), /* ExitProcess(UINT uExitCode) argument */
114 0, /* value of 0 tells thread to run immediately
116 &dwTID
); /* new remote thread's identifier */
120 bool bHasExited
= false;
124 WaitForSingleObject(hProcess
, INFINITE
); // wait for process to terminate, never stop waiting...
125 CloseHandle(hRemoteThread
); // close the thread handle to allow the process to exit
129 // need to close this duplicated process handle...
131 CloseHandle(hProcess
);
134 return osl_Process_E_None
;
136 // fallback - given that we wait for an infinite time on WaitForSingleObject, this should
137 // never occur... unless CreateRemoteThread failed
138 SAL_WARN("sal.osl", "TerminateProcess(hProcess, 0) called - we should never get here!");
139 return (TerminateProcess(hProcess
, 0) == FALSE
) ? osl_Process_E_Unknown
: osl_Process_E_None
;
142 oslProcess SAL_CALL
osl_getProcess(oslProcessIdentifier Ident
)
144 oslProcessImpl
* pProcImpl
;
145 HANDLE hProcess
= OpenProcess(
146 STANDARD_RIGHTS_REQUIRED
| PROCESS_QUERY_INFORMATION
| SYNCHRONIZE
, FALSE
, static_cast<DWORD
>(Ident
));
150 pProcImpl
= static_cast< oslProcessImpl
*>( malloc(sizeof(oslProcessImpl
)) );
151 pProcImpl
->m_hProcess
= hProcess
;
152 pProcImpl
->m_IdProcess
= Ident
;
160 void SAL_CALL
osl_freeProcessHandle(oslProcess Process
)
162 if (Process
!= nullptr)
164 CloseHandle(static_cast<oslProcessImpl
*>(Process
)->m_hProcess
);
170 oslProcessError SAL_CALL
osl_getProcessInfo(oslProcess Process
, oslProcessData Fields
,
171 oslProcessInfo
* pInfo
)
176 if (Process
== nullptr)
178 hProcess
= GetCurrentProcess();
179 IdProcess
= GetCurrentProcessId();
183 hProcess
= static_cast<oslProcessImpl
*>(Process
)->m_hProcess
;
184 IdProcess
= static_cast<oslProcessImpl
*>(Process
)->m_IdProcess
;
187 if (! pInfo
|| (pInfo
->Size
!= sizeof(oslProcessInfo
)))
188 return osl_Process_E_Unknown
;
192 if (Fields
& osl_Process_IDENTIFIER
)
194 pInfo
->Ident
= IdProcess
;
195 pInfo
->Fields
|= osl_Process_IDENTIFIER
;
198 if (Fields
& osl_Process_EXITCODE
)
200 if (GetExitCodeProcess(hProcess
, &(pInfo
->Code
)) && (pInfo
->Code
!= STILL_ACTIVE
))
201 pInfo
->Fields
|= osl_Process_EXITCODE
;
204 if (Fields
& osl_Process_HEAPUSAGE
)
206 void* lpAddress
=nullptr;
207 MEMORY_BASIC_INFORMATION Info
;
209 pInfo
->HeapUsage
= 0;
213 if (VirtualQueryEx(hProcess
, lpAddress
, &Info
, sizeof(Info
)) == 0)
216 if ((Info
.State
== MEM_COMMIT
) && (Info
.Type
== MEM_PRIVATE
))
217 pInfo
->HeapUsage
+= Info
.RegionSize
;
219 lpAddress
= static_cast<LPBYTE
>(lpAddress
) + Info
.RegionSize
;
221 while (reinterpret_cast<uintptr_t>(lpAddress
) <= uintptr_t(0x7FFFFFFF)); // 2GB address space
223 pInfo
->Fields
|= osl_Process_HEAPUSAGE
;
226 if (Fields
& osl_Process_CPUTIMES
)
228 FILETIME CreationTime
, ExitTime
, KernelTime
, UserTime
;
230 if (GetProcessTimes(hProcess
, &CreationTime
, &ExitTime
,
231 &KernelTime
, &UserTime
))
235 Value
= osl::detail::getFiletime(UserTime
);
236 pInfo
->UserTime
.Seconds
= static_cast<unsigned long>(Value
/ 10000000L);
237 pInfo
->UserTime
.Nanosec
= static_cast<unsigned long>((Value
% 10000000L) * 100);
239 Value
= osl::detail::getFiletime(KernelTime
);
240 pInfo
->SystemTime
.Seconds
= static_cast<unsigned long>(Value
/ 10000000L);
241 pInfo
->SystemTime
.Nanosec
= static_cast<unsigned long>((Value
% 10000000L) * 100);
243 pInfo
->Fields
|= osl_Process_CPUTIMES
;
247 return (pInfo
->Fields
== Fields
) ? osl_Process_E_None
: osl_Process_E_Unknown
;
250 oslProcessError SAL_CALL
osl_joinProcess(oslProcess Process
)
252 return osl_joinProcessWithTimeout(Process
, nullptr);
255 oslProcessError SAL_CALL
osl_joinProcessWithTimeout(oslProcess Process
, const TimeValue
* pTimeout
)
257 DWORD timeout
= INFINITE
;
258 oslProcessError osl_error
= osl_Process_E_None
;
261 if (nullptr == Process
)
262 return osl_Process_E_Unknown
;
265 timeout
= pTimeout
->Seconds
* 1000 + pTimeout
->Nanosec
/ 1000000L;
267 ret
= WaitForSingleObject(static_cast<oslProcessImpl
*>(Process
)->m_hProcess
, timeout
);
269 if (WAIT_FAILED
== ret
)
270 osl_error
= osl_Process_E_Unknown
;
271 else if (WAIT_TIMEOUT
== ret
)
272 osl_error
= osl_Process_E_TimedOut
;
279 oslProcessError
bootstrap_getExecutableFile(rtl_uString
** ppFileURL
)
281 oslProcessError result
= osl_Process_E_NotFound
;
283 ::osl::LongPathBuffer
< sal_Unicode
> aBuffer( MAX_LONG_PATH
);
286 if ((buflen
= GetModuleFileNameW (nullptr, o3tl::toW(aBuffer
), aBuffer
.getBufSizeInSymbols())) > 0)
288 rtl_uString
* pAbsPath
= nullptr;
289 rtl_uString_newFromStr_WithLength (&pAbsPath
, aBuffer
, buflen
);
292 /* Convert from path to url. */
293 if (osl_getFileURLFromSystemPath (pAbsPath
, ppFileURL
) == osl_File_E_None
)
296 result
= osl_Process_E_None
;
298 rtl_uString_release (pAbsPath
);
307 struct CommandArgs_Impl
310 rtl_uString
** m_ppArgs
;
313 static struct CommandArgs_Impl g_command_args
=
319 static rtl_uString
** osl_createCommandArgs_Impl (int argc
, char **)
321 rtl_uString
** ppArgs
=
322 static_cast<rtl_uString
**>(rtl_allocateZeroMemory (argc
* sizeof(rtl_uString
*)));
323 if (ppArgs
!= nullptr)
327 LPWSTR
*wargv
= CommandLineToArgvW( GetCommandLineW(), &nArgs
);
328 assert( nArgs
== argc
);
329 for (i
= 0; i
< nArgs
; i
++)
331 /* Convert to unicode */
332 rtl_uString_newFromStr( &(ppArgs
[i
]), o3tl::toU(wargv
[i
]) );
334 if (ppArgs
[0] != nullptr)
336 /* Ensure absolute path */
337 ::osl::LongPathBuffer
< sal_Unicode
> aBuffer( MAX_LONG_PATH
);
340 dwResult
= SearchPathW (
341 nullptr, o3tl::toW(ppArgs
[0]->buffer
), L
".exe", aBuffer
.getBufSizeInSymbols(), o3tl::toW(aBuffer
), nullptr);
342 if ((0 < dwResult
) && (dwResult
< aBuffer
.getBufSizeInSymbols()))
344 /* Replace argv[0] with its absolute path */
345 rtl_uString_newFromStr_WithLength(
346 &(ppArgs
[0]), aBuffer
, dwResult
);
349 if (ppArgs
[0] != nullptr)
351 /* Convert to FileURL, see @ osl_getExecutableFile() */
352 rtl_uString
* pResult
= nullptr;
353 osl_getFileURLFromSystemPath (ppArgs
[0], &pResult
);
354 if (pResult
!= nullptr)
356 rtl_uString_assign (&(ppArgs
[0]), pResult
);
357 rtl_uString_release (pResult
);
365 oslProcessError SAL_CALL
osl_getExecutableFile( rtl_uString
**ppustrFile
)
367 osl_acquireMutex (*osl_getGlobalMutex());
368 if (g_command_args
.m_nCount
== 0)
370 osl_releaseMutex (*osl_getGlobalMutex());
371 return bootstrap_getExecutableFile(ppustrFile
);
374 /* CommandArgs set. Obtain arv[0]. */
375 rtl_uString_assign (ppustrFile
, g_command_args
.m_ppArgs
[0]);
376 osl_releaseMutex (*osl_getGlobalMutex());
377 return osl_Process_E_None
;
380 sal_uInt32 SAL_CALL
osl_getCommandArgCount()
382 sal_uInt32 result
= 0;
384 osl_acquireMutex (*osl_getGlobalMutex());
386 g_command_args
.m_nCount
== 0, "sal.osl",
387 "osl_getCommandArgCount w/o prior call to osl_setCommandArgs");
388 if (g_command_args
.m_nCount
> 0)
390 /* We're not counting argv[0] here. */
391 result
= g_command_args
.m_nCount
- 1;
393 osl_releaseMutex (*osl_getGlobalMutex());
398 oslProcessError SAL_CALL
osl_getCommandArg( sal_uInt32 nArg
, rtl_uString
**strCommandArg
)
400 oslProcessError result
= osl_Process_E_NotFound
;
402 osl_acquireMutex (*osl_getGlobalMutex());
403 assert(g_command_args
.m_nCount
> 0);
404 if (g_command_args
.m_nCount
> (nArg
+ 1))
406 /* We're not counting argv[0] here. */
407 rtl_uString_assign (strCommandArg
, g_command_args
.m_ppArgs
[nArg
+ 1]);
408 result
= osl_Process_E_None
;
410 osl_releaseMutex (*osl_getGlobalMutex());
415 void SAL_CALL
osl_setCommandArgs (int argc
, char ** argv
)
418 osl_acquireMutex (*osl_getGlobalMutex());
419 SAL_WARN_IF(g_command_args
.m_nCount
!= 0, "sal.osl", "args already set");
420 if (g_command_args
.m_nCount
== 0)
422 rtl_uString
** ppArgs
= osl_createCommandArgs_Impl (argc
, argv
);
423 if (ppArgs
!= nullptr)
425 g_command_args
.m_nCount
= argc
;
426 g_command_args
.m_ppArgs
= ppArgs
;
429 osl_releaseMutex (*osl_getGlobalMutex());
432 /* TODO because of an issue with GetEnvironmentVariableW we have to
433 allocate a buffer large enough to hold the requested environment
434 variable instead of testing for the required size. This wastes
435 some stack space, maybe we should revoke this work around if
436 this is no longer a problem */
437 #define ENV_BUFFER_SIZE (32*1024-1)
439 oslProcessError SAL_CALL
osl_getEnvironment(rtl_uString
*ustrVar
, rtl_uString
**ustrValue
)
441 WCHAR buff
[ENV_BUFFER_SIZE
];
443 if (GetEnvironmentVariableW(o3tl::toW(ustrVar
->buffer
), buff
, ENV_BUFFER_SIZE
) > 0)
445 rtl_uString_newFromStr(ustrValue
, o3tl::toU(buff
));
446 return osl_Process_E_None
;
448 return osl_Process_E_Unknown
;
451 oslProcessError SAL_CALL
osl_setEnvironment(rtl_uString
*ustrVar
, rtl_uString
*ustrValue
)
453 // set Windows environment variable
454 LPCWSTR lpName
= o3tl::toW(ustrVar
->buffer
);
455 LPCWSTR lpValue
= o3tl::toW(ustrValue
->buffer
);
456 if (SetEnvironmentVariableW(lpName
, lpValue
))
458 auto buffer
= std::unique_ptr
<wchar_t[]>(
459 new wchar_t[wcslen(lpName
) + 1 + wcslen(lpValue
) + 1]);
460 wcscpy(buffer
.get(), lpName
);
461 wcscat(buffer
.get(), L
"=");
462 wcscat(buffer
.get(), lpValue
);
463 _wputenv(buffer
.get());
464 return osl_Process_E_None
;
466 return osl_Process_E_Unknown
;
469 oslProcessError SAL_CALL
osl_clearEnvironment(rtl_uString
*ustrVar
)
471 // delete the variable from the current process environment
472 // by setting SetEnvironmentVariable's second parameter to NULL
473 LPCWSTR lpName
= o3tl::toW(ustrVar
->buffer
);
474 if (SetEnvironmentVariableW(lpName
, nullptr))
476 auto buffer
= std::unique_ptr
<wchar_t[]>(
477 new wchar_t[wcslen(lpName
) + 1 + 1]);
478 wcscpy(buffer
.get(), lpName
);
479 wcscat(buffer
.get(), L
"=");
480 _wputenv(buffer
.get());
481 return osl_Process_E_None
;
483 return osl_Process_E_Unknown
;
486 oslProcessError SAL_CALL
osl_getProcessWorkingDir( rtl_uString
**pustrWorkingDir
)
488 ::osl::LongPathBuffer
< sal_Unicode
> aBuffer( MAX_LONG_PATH
);
491 osl_acquireMutex( g_CurrentDirectoryMutex
);
492 dwLen
= GetCurrentDirectoryW( aBuffer
.getBufSizeInSymbols(), o3tl::toW(aBuffer
) );
493 osl_releaseMutex( g_CurrentDirectoryMutex
);
495 if ( dwLen
&& dwLen
< aBuffer
.getBufSizeInSymbols() )
498 rtl_uString
*ustrTemp
= nullptr;
500 rtl_uString_newFromStr_WithLength( &ustrTemp
, aBuffer
, dwLen
);
501 eError
= osl_getFileURLFromSystemPath( ustrTemp
, pustrWorkingDir
);
503 rtl_uString_release( ustrTemp
);
505 if ( osl_File_E_None
!= eError
)
506 return osl_Process_E_Unknown
;
508 return osl_Process_E_None
;
511 return osl_Process_E_Unknown
;
514 static rtl_Locale
* g_theProcessLocale
= nullptr;
516 oslProcessError SAL_CALL
osl_getProcessLocale( rtl_Locale
** ppLocale
)
518 osl_acquireMutex( *osl_getGlobalMutex() );
520 /* determine the users default locale */
521 if( nullptr == g_theProcessLocale
)
522 imp_getProcessLocale( &g_theProcessLocale
);
524 /* or return the cached value */
525 *ppLocale
= g_theProcessLocale
;
527 osl_releaseMutex( *osl_getGlobalMutex() );
528 return osl_Process_E_None
;
531 oslProcessError SAL_CALL
osl_setProcessLocale( rtl_Locale
* pLocale
)
533 osl_acquireMutex( *osl_getGlobalMutex() );
535 /* check if locale is supported */
536 if( RTL_TEXTENCODING_DONTKNOW
== osl_getTextEncodingFromLocale( pLocale
) )
537 return osl_Process_E_Unknown
;
539 /* just remember the locale here */
540 g_theProcessLocale
= pLocale
;
542 osl_releaseMutex( *osl_getGlobalMutex() );
543 return osl_Process_E_None
;
546 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */