Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sal / osl / w32 / process.cxx
blob065415f2cf860acda9c92d7184f23f124c5f7149
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 "system.h"
21 #include <string.h>
23 #include <shellapi.h>
25 #include <cassert>
27 #include <osl/mutex.hxx>
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 <rtl/ustring.hxx>
38 #include <sal/log.hxx>
40 oslProcessError SAL_CALL osl_terminateProcess(oslProcess Process)
42 if (Process == nullptr)
43 return osl_Process_E_Unknown;
45 HANDLE hProcess = static_cast<oslProcessImpl*>(Process)->m_hProcess;
46 DWORD dwPID = GetProcessId(hProcess);
48 // cannot be System Process (0x00000000)
49 if (dwPID == 0x0)
50 return osl_Process_E_InvalidError;
52 // Test to see if we can create a thread in a process... adapted from:
53 // * https://support.microsoft.com/en-us/help/178893/how-to-terminate-an-application-cleanly-in-win32
54 // * http://www.drdobbs.com/a-safer-alternative-to-terminateprocess/184416547
56 // TODO: we really should firstly check to see if we have access to create threads and only
57 // duplicate the handle with elevated access if we don't have access... this can be done, but
58 // it's not exactly easy - an example can be found here:
59 // http://windowsitpro.com/site-files/windowsitpro.com/files/archive/windowsitpro.com/content/content/15989/listing_01.txt
61 HANDLE hDupProcess = nullptr;
64 // we need to make sure we can create a thread in the remote process, if the handle was created
65 // in something that doesn't give us appropriate levels of access then we will need to give it the
66 // desired level of access - if the process handle was grabbed from OpenProcess it's quite possible
67 // that the handle doesn't have the appropriate level of access...
69 // see https://msdn.microsoft.com/en-au/library/windows/desktop/ms684880(v=vs.85).aspx
70 DWORD const dwAccessFlags = (PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION
71 | PROCESS_VM_WRITE | PROCESS_VM_READ);
73 bool bHaveDuplHdl = DuplicateHandle(GetCurrentProcess(), // handle to process that has handle
74 hProcess, // handle to be duplicated
75 GetCurrentProcess(), // process that will get the dup handle
76 &hDupProcess, // store duplicate process handle here
77 dwAccessFlags, // desired access
78 FALSE, // handle can't be inherited
79 0); // zero means no additional action needed
81 if (bHaveDuplHdl)
82 hProcess = hDupProcess; // so we were able to duplicate the handle, all good...
83 else
84 SAL_WARN("sal.osl", "Could not duplicate process handle, let's hope for the best...");
86 DWORD dwProcessStatus = 0;
87 HANDLE hRemoteThread = nullptr;
89 if (GetExitCodeProcess(hProcess, &dwProcessStatus) && (dwProcessStatus == STILL_ACTIVE))
91 // We need to get the address of the Win32 procedure ExitProcess, can't call it
92 // directly because we'll be calling the thunk and that will probably lead to an
93 // access violation. Once we have the address, then we need to create a new
94 // thread in the process (which we might need to run in the address space of
95 // another process) and then call on ExitProcess to try to cleanly terminate that
96 // process
98 DWORD dwTID = 0; // dummy variable as we don't need to track the thread ID
100 // Note: we want to call on ExitProcess() and not TerminateProcess() - this is
101 // because with ExitProcess() Windows notifies all attached dlls that the process
102 // is detaching from the dll, but TerminateProcess() terminates all threads
103 // immediately, doesn't call any termination handlers and doesn't notify any dlls
104 // that it is detaching from them
106 HINSTANCE hKernel = GetModuleHandleW(L"kernel32.dll");
107 FARPROC pfnExitProc = GetProcAddress(hKernel, "ExitProcess");
108 hRemoteThread = CreateRemoteThread(
109 hProcess, /* process handle */
110 nullptr, /* default security descriptor */
111 0, /* initial size of stack in bytes is default
112 size for executable */
113 reinterpret_cast<LPTHREAD_START_ROUTINE>(pfnExitProc), /* Win32 ExitProcess() */
114 reinterpret_cast<PVOID>(UINT(0)), /* ExitProcess(UINT uExitCode) argument */
115 0, /* value of 0 tells thread to run immediately
116 after creation */
117 &dwTID); /* new remote thread's identifier */
121 bool bHasExited = false;
123 if (hRemoteThread)
125 WaitForSingleObject(hProcess, INFINITE); // wait for process to terminate, never stop waiting...
126 CloseHandle(hRemoteThread); // close the thread handle to allow the process to exit
127 bHasExited = true;
130 // need to close this duplicated process handle...
131 if (bHaveDuplHdl)
132 CloseHandle(hProcess);
134 if (bHasExited)
135 return osl_Process_E_None;
137 // fallback - given that we wait for an infinite time on WaitForSingleObject, this should
138 // never occur... unless CreateRemoteThread failed
139 SAL_WARN("sal.osl", "TerminateProcess(hProcess, 0) called - we should never get here!");
140 return (TerminateProcess(hProcess, 0) == FALSE) ? osl_Process_E_Unknown : osl_Process_E_None;
143 oslProcess SAL_CALL osl_getProcess(oslProcessIdentifier Ident)
145 oslProcessImpl* pProcImpl;
146 HANDLE hProcess = OpenProcess(
147 STANDARD_RIGHTS_REQUIRED | PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, static_cast<DWORD>(Ident));
149 if (hProcess)
151 pProcImpl = static_cast< oslProcessImpl*>( malloc(sizeof(oslProcessImpl)) );
152 pProcImpl->m_hProcess = hProcess;
153 pProcImpl->m_IdProcess = Ident;
155 else
156 pProcImpl = nullptr;
158 return pProcImpl;
161 void SAL_CALL osl_freeProcessHandle(oslProcess Process)
163 if (Process != nullptr)
165 CloseHandle(static_cast<oslProcessImpl*>(Process)->m_hProcess);
167 free(Process);
171 oslProcessError SAL_CALL osl_getProcessInfo(oslProcess Process, oslProcessData Fields,
172 oslProcessInfo* pInfo)
174 HANDLE hProcess;
175 DWORD IdProcess;
177 if (Process == nullptr)
179 hProcess = GetCurrentProcess();
180 IdProcess = GetCurrentProcessId();
182 else
184 hProcess = static_cast<oslProcessImpl*>(Process)->m_hProcess;
185 IdProcess = static_cast<oslProcessImpl*>(Process)->m_IdProcess;
188 if (! pInfo || (pInfo->Size != sizeof(oslProcessInfo)))
189 return osl_Process_E_Unknown;
191 pInfo->Fields = 0;
193 if (Fields & osl_Process_IDENTIFIER)
195 pInfo->Ident = IdProcess;
196 pInfo->Fields |= osl_Process_IDENTIFIER;
199 if (Fields & osl_Process_EXITCODE)
201 if (GetExitCodeProcess(hProcess, &(pInfo->Code)) && (pInfo->Code != STILL_ACTIVE))
202 pInfo->Fields |= osl_Process_EXITCODE;
205 if (Fields & osl_Process_HEAPUSAGE)
207 void* lpAddress=nullptr;
208 MEMORY_BASIC_INFORMATION Info;
210 pInfo->HeapUsage = 0;
214 if (VirtualQueryEx(hProcess, lpAddress, &Info, sizeof(Info)) == 0)
215 break;
217 if ((Info.State == MEM_COMMIT) && (Info.Type == MEM_PRIVATE))
218 pInfo->HeapUsage += Info.RegionSize;
220 lpAddress = static_cast<LPBYTE>(lpAddress) + Info.RegionSize;
222 while (reinterpret_cast<uintptr_t>(lpAddress) <= 0x7FFFFFFFU); // 2GB address space
224 pInfo->Fields |= osl_Process_HEAPUSAGE;
227 if (Fields & osl_Process_CPUTIMES)
229 FILETIME CreationTime, ExitTime, KernelTime, UserTime;
231 if (GetProcessTimes(hProcess, &CreationTime, &ExitTime,
232 &KernelTime, &UserTime))
234 __int64 Value;
236 Value = osl::detail::getFiletime(UserTime);
237 pInfo->UserTime.Seconds = static_cast<unsigned long>(Value / 10000000L);
238 pInfo->UserTime.Nanosec = static_cast<unsigned long>((Value % 10000000L) * 100);
240 Value = osl::detail::getFiletime(KernelTime);
241 pInfo->SystemTime.Seconds = static_cast<unsigned long>(Value / 10000000L);
242 pInfo->SystemTime.Nanosec = static_cast<unsigned long>((Value % 10000000L) * 100);
244 pInfo->Fields |= osl_Process_CPUTIMES;
248 return (pInfo->Fields == Fields) ? osl_Process_E_None : osl_Process_E_Unknown;
251 oslProcessError SAL_CALL osl_joinProcess(oslProcess Process)
253 return osl_joinProcessWithTimeout(Process, nullptr);
256 oslProcessError SAL_CALL osl_joinProcessWithTimeout(oslProcess Process, const TimeValue* pTimeout)
258 DWORD timeout = INFINITE;
259 oslProcessError osl_error = osl_Process_E_None;
260 DWORD ret;
262 if (nullptr == Process)
263 return osl_Process_E_Unknown;
265 if (pTimeout)
266 timeout = pTimeout->Seconds * 1000 + pTimeout->Nanosec / 1000000L;
268 ret = WaitForSingleObject(static_cast<oslProcessImpl*>(Process)->m_hProcess, timeout);
270 if (WAIT_FAILED == ret)
271 osl_error = osl_Process_E_Unknown;
272 else if (WAIT_TIMEOUT == ret)
273 osl_error = osl_Process_E_TimedOut;
275 return osl_error;
278 namespace {
280 oslProcessError bootstrap_getExecutableFile(rtl_uString ** ppFileURL)
282 oslProcessError result = osl_Process_E_NotFound;
284 ::osl::LongPathBuffer< sal_Unicode > aBuffer( MAX_LONG_PATH );
285 DWORD buflen = 0;
287 if ((buflen = GetModuleFileNameW (nullptr, o3tl::toW(aBuffer), aBuffer.getBufSizeInSymbols())) > 0)
289 rtl_uString * pAbsPath = nullptr;
290 rtl_uString_newFromStr_WithLength (&pAbsPath, aBuffer, buflen);
291 if (pAbsPath)
293 /* Convert from path to url. */
294 if (osl_getFileURLFromSystemPath (pAbsPath, ppFileURL) == osl_File_E_None)
296 /* Success. */
297 result = osl_Process_E_None;
299 rtl_uString_release (pAbsPath);
303 return result;
306 struct CommandArgs_Impl
308 sal_uInt32 m_nCount;
309 rtl_uString ** m_ppArgs;
314 static struct CommandArgs_Impl g_command_args =
317 nullptr
320 static rtl_uString ** osl_createCommandArgs_Impl (int argc, char **)
322 rtl_uString ** ppArgs =
323 static_cast<rtl_uString**>(rtl_allocateZeroMemory (argc * sizeof(rtl_uString*)));
324 if (ppArgs != nullptr)
326 int i;
327 int nArgs;
328 LPWSTR *wargv = CommandLineToArgvW( GetCommandLineW(), &nArgs );
329 assert( nArgs == argc );
330 for (i = 0; i < nArgs; i++)
332 /* Convert to unicode */
333 rtl_uString_newFromStr( &(ppArgs[i]), o3tl::toU(wargv[i]) );
335 if (ppArgs[0] != nullptr)
337 /* Ensure absolute path */
338 ::osl::LongPathBuffer< sal_Unicode > aBuffer( MAX_LONG_PATH );
339 DWORD dwResult
340 = GetModuleFileNameW(nullptr, o3tl::toW(aBuffer), aBuffer.getBufSizeInSymbols());
341 if ((0 < dwResult) && (dwResult < aBuffer.getBufSizeInSymbols()))
343 /* Replace argv[0] with its absolute path */
344 rtl_uString_newFromStr_WithLength(
345 &(ppArgs[0]), aBuffer, dwResult);
348 if (ppArgs[0] != nullptr)
350 /* Convert to FileURL, see @ osl_getExecutableFile() */
351 rtl_uString * pResult = nullptr;
352 osl_getFileURLFromSystemPath (ppArgs[0], &pResult);
353 if (pResult != nullptr)
355 rtl_uString_assign (&(ppArgs[0]), pResult);
356 rtl_uString_release (pResult);
360 return ppArgs;
364 oslProcessError SAL_CALL osl_getExecutableFile( rtl_uString **ppustrFile )
367 osl::MutexGuard aGuard(osl::Mutex::getGlobalMutex());
368 if (g_command_args.m_nCount > 0)
370 /* CommandArgs set. Obtain arv[0]. */
371 rtl_uString_assign(ppustrFile, g_command_args.m_ppArgs[0]);
372 return osl_Process_E_None;
375 return bootstrap_getExecutableFile(ppustrFile);
378 sal_uInt32 SAL_CALL osl_getCommandArgCount()
380 sal_uInt32 result = 0;
382 osl::MutexGuard aGuard(osl::Mutex::getGlobalMutex());
383 SAL_INFO_IF(
384 g_command_args.m_nCount == 0, "sal.osl",
385 "osl_getCommandArgCount w/o prior call to osl_setCommandArgs");
386 if (g_command_args.m_nCount > 0)
388 /* We're not counting argv[0] here. */
389 result = g_command_args.m_nCount - 1;
392 return result;
395 oslProcessError SAL_CALL osl_getCommandArg( sal_uInt32 nArg, rtl_uString **strCommandArg)
397 oslProcessError result = osl_Process_E_NotFound;
399 osl::MutexGuard aGuard(osl::Mutex::getGlobalMutex());
400 assert(g_command_args.m_nCount > 0);
401 if (g_command_args.m_nCount > (nArg + 1))
403 /* We're not counting argv[0] here. */
404 rtl_uString_assign (strCommandArg, g_command_args.m_ppArgs[nArg + 1]);
405 result = osl_Process_E_None;
408 return result;
411 void SAL_CALL osl_setCommandArgs (int argc, char ** argv)
413 assert(argc > 0);
414 osl::MutexGuard aGuard(osl::Mutex::getGlobalMutex());
415 SAL_WARN_IF(g_command_args.m_nCount != 0, "sal.osl", "args already set");
416 if (g_command_args.m_nCount == 0)
418 rtl_uString** ppArgs = osl_createCommandArgs_Impl (argc, argv);
419 if (ppArgs != nullptr)
421 g_command_args.m_nCount = argc;
422 g_command_args.m_ppArgs = ppArgs;
427 /* TODO because of an issue with GetEnvironmentVariableW we have to
428 allocate a buffer large enough to hold the requested environment
429 variable instead of testing for the required size. This wastes
430 some stack space, maybe we should revoke this work around if
431 this is no longer a problem */
432 #define ENV_BUFFER_SIZE (32*1024-1)
434 oslProcessError SAL_CALL osl_getEnvironment(rtl_uString *ustrVar, rtl_uString **ustrValue)
436 WCHAR buff[ENV_BUFFER_SIZE];
438 if (GetEnvironmentVariableW(o3tl::toW(ustrVar->buffer), buff, ENV_BUFFER_SIZE) > 0)
440 rtl_uString_newFromStr(ustrValue, o3tl::toU(buff));
441 return osl_Process_E_None;
443 return osl_Process_E_Unknown;
446 oslProcessError SAL_CALL osl_setEnvironment(rtl_uString *ustrVar, rtl_uString *ustrValue)
448 // set Windows environment variable
449 if (SetEnvironmentVariableW(o3tl::toW(ustrVar->buffer), o3tl::toW(ustrValue->buffer)))
451 OUString sAssign = OUString::unacquired(&ustrVar) + "=" + OUString::unacquired(&ustrValue);
452 _wputenv(o3tl::toW(sAssign.getStr()));
453 return osl_Process_E_None;
455 return osl_Process_E_Unknown;
458 oslProcessError SAL_CALL osl_clearEnvironment(rtl_uString *ustrVar)
460 // delete the variable from the current process environment
461 // by setting SetEnvironmentVariable's second parameter to NULL
462 if (SetEnvironmentVariableW(o3tl::toW(ustrVar->buffer), nullptr))
464 OUString sAssign = OUString::unacquired(&ustrVar) + "=";
465 _wputenv(o3tl::toW(sAssign.getStr()));
466 return osl_Process_E_None;
468 return osl_Process_E_Unknown;
471 oslProcessError SAL_CALL osl_getProcessWorkingDir( rtl_uString **pustrWorkingDir )
473 ::osl::LongPathBuffer< sal_Unicode > aBuffer( MAX_LONG_PATH );
474 DWORD dwLen = GetCurrentDirectoryW(aBuffer.getBufSizeInSymbols(), o3tl::toW(aBuffer));
476 if ( dwLen && dwLen < aBuffer.getBufSizeInSymbols() )
478 oslFileError eError;
479 rtl_uString *ustrTemp = nullptr;
481 rtl_uString_newFromStr_WithLength( &ustrTemp, aBuffer, dwLen );
482 eError = osl_getFileURLFromSystemPath( ustrTemp, pustrWorkingDir );
484 rtl_uString_release( ustrTemp );
486 if ( osl_File_E_None != eError )
487 return osl_Process_E_Unknown;
488 else
489 return osl_Process_E_None;
491 else
492 return osl_Process_E_Unknown;
495 static rtl_Locale * g_theProcessLocale = nullptr;
497 oslProcessError SAL_CALL osl_getProcessLocale( rtl_Locale ** ppLocale )
499 osl::MutexGuard aGuard(osl::Mutex::getGlobalMutex());
501 /* determine the users default locale */
502 if( nullptr == g_theProcessLocale )
503 imp_getProcessLocale( &g_theProcessLocale );
505 /* or return the cached value */
506 *ppLocale = g_theProcessLocale;
508 return osl_Process_E_None;
511 oslProcessError SAL_CALL osl_setProcessLocale( rtl_Locale * pLocale )
513 osl::MutexGuard aGuard(osl::Mutex::getGlobalMutex());
515 /* check if locale is supported */
516 if( RTL_TEXTENCODING_DONTKNOW == osl_getTextEncodingFromLocale( pLocale ) )
517 return osl_Process_E_Unknown;
519 /* just remember the locale here */
520 g_theProcessLocale = pLocale;
522 return osl_Process_E_None;
525 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */