Branch libreoffice-6-3
[LibreOffice.git] / sal / osl / w32 / process.cxx
blobcc5c1568f2f4b433d28f6a336d6b018e2600606d
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>
26 #include <memory>
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)
48 if (dwPID == 0x0)
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
80 if (bHaveDuplHdl)
81 hProcess = hDupProcess; // so we were able to duplicate the handle, all good...
82 else
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
95 // process
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
115 after creation */
116 &dwTID); /* new remote thread's identifier */
120 bool bHasExited = false;
122 if (hRemoteThread)
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
126 bHasExited = true;
129 // need to close this duplicated process handle...
130 if (bHaveDuplHdl)
131 CloseHandle(hProcess);
133 if (bHasExited)
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));
148 if (hProcess)
150 pProcImpl = static_cast< oslProcessImpl*>( malloc(sizeof(oslProcessImpl)) );
151 pProcImpl->m_hProcess = hProcess;
152 pProcImpl->m_IdProcess = Ident;
154 else
155 pProcImpl = nullptr;
157 return pProcImpl;
160 void SAL_CALL osl_freeProcessHandle(oslProcess Process)
162 if (Process != nullptr)
164 CloseHandle(static_cast<oslProcessImpl*>(Process)->m_hProcess);
166 free(Process);
170 oslProcessError SAL_CALL osl_getProcessInfo(oslProcess Process, oslProcessData Fields,
171 oslProcessInfo* pInfo)
173 HANDLE hProcess;
174 DWORD IdProcess;
176 if (Process == nullptr)
178 hProcess = GetCurrentProcess();
179 IdProcess = GetCurrentProcessId();
181 else
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;
190 pInfo->Fields = 0;
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)
214 break;
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))
233 __int64 Value;
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;
259 DWORD ret;
261 if (nullptr == Process)
262 return osl_Process_E_Unknown;
264 if (pTimeout)
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;
274 return osl_error;
277 namespace {
279 oslProcessError bootstrap_getExecutableFile(rtl_uString ** ppFileURL)
281 oslProcessError result = osl_Process_E_NotFound;
283 ::osl::LongPathBuffer< sal_Unicode > aBuffer( MAX_LONG_PATH );
284 DWORD buflen = 0;
286 if ((buflen = GetModuleFileNameW (nullptr, o3tl::toW(aBuffer), aBuffer.getBufSizeInSymbols())) > 0)
288 rtl_uString * pAbsPath = nullptr;
289 rtl_uString_newFromStr_WithLength (&pAbsPath, aBuffer, buflen);
290 if (pAbsPath)
292 /* Convert from path to url. */
293 if (osl_getFileURLFromSystemPath (pAbsPath, ppFileURL) == osl_File_E_None)
295 /* Success. */
296 result = osl_Process_E_None;
298 rtl_uString_release (pAbsPath);
302 return result;
307 struct CommandArgs_Impl
309 sal_uInt32 m_nCount;
310 rtl_uString ** m_ppArgs;
313 static struct CommandArgs_Impl g_command_args =
316 nullptr
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)
325 int i;
326 int nArgs;
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 );
338 DWORD dwResult
339 = GetModuleFileNameW(nullptr, o3tl::toW(aBuffer), aBuffer.getBufSizeInSymbols());
340 if ((0 < dwResult) && (dwResult < aBuffer.getBufSizeInSymbols()))
342 /* Replace argv[0] with its absolute path */
343 rtl_uString_newFromStr_WithLength(
344 &(ppArgs[0]), aBuffer, dwResult);
347 if (ppArgs[0] != nullptr)
349 /* Convert to FileURL, see @ osl_getExecutableFile() */
350 rtl_uString * pResult = nullptr;
351 osl_getFileURLFromSystemPath (ppArgs[0], &pResult);
352 if (pResult != nullptr)
354 rtl_uString_assign (&(ppArgs[0]), pResult);
355 rtl_uString_release (pResult);
359 return ppArgs;
363 oslProcessError SAL_CALL osl_getExecutableFile( rtl_uString **ppustrFile )
365 osl_acquireMutex (*osl_getGlobalMutex());
366 if (g_command_args.m_nCount == 0)
368 osl_releaseMutex (*osl_getGlobalMutex());
369 return bootstrap_getExecutableFile(ppustrFile);
372 /* CommandArgs set. Obtain arv[0]. */
373 rtl_uString_assign (ppustrFile, g_command_args.m_ppArgs[0]);
374 osl_releaseMutex (*osl_getGlobalMutex());
375 return osl_Process_E_None;
378 sal_uInt32 SAL_CALL osl_getCommandArgCount()
380 sal_uInt32 result = 0;
382 osl_acquireMutex (*osl_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;
391 osl_releaseMutex (*osl_getGlobalMutex());
393 return result;
396 oslProcessError SAL_CALL osl_getCommandArg( sal_uInt32 nArg, rtl_uString **strCommandArg)
398 oslProcessError result = osl_Process_E_NotFound;
400 osl_acquireMutex (*osl_getGlobalMutex());
401 assert(g_command_args.m_nCount > 0);
402 if (g_command_args.m_nCount > (nArg + 1))
404 /* We're not counting argv[0] here. */
405 rtl_uString_assign (strCommandArg, g_command_args.m_ppArgs[nArg + 1]);
406 result = osl_Process_E_None;
408 osl_releaseMutex (*osl_getGlobalMutex());
410 return result;
413 void SAL_CALL osl_setCommandArgs (int argc, char ** argv)
415 assert(argc > 0);
416 osl_acquireMutex (*osl_getGlobalMutex());
417 SAL_WARN_IF(g_command_args.m_nCount != 0, "sal.osl", "args already set");
418 if (g_command_args.m_nCount == 0)
420 rtl_uString** ppArgs = osl_createCommandArgs_Impl (argc, argv);
421 if (ppArgs != nullptr)
423 g_command_args.m_nCount = argc;
424 g_command_args.m_ppArgs = ppArgs;
427 osl_releaseMutex (*osl_getGlobalMutex());
430 /* TODO because of an issue with GetEnvironmentVariableW we have to
431 allocate a buffer large enough to hold the requested environment
432 variable instead of testing for the required size. This wastes
433 some stack space, maybe we should revoke this work around if
434 this is no longer a problem */
435 #define ENV_BUFFER_SIZE (32*1024-1)
437 oslProcessError SAL_CALL osl_getEnvironment(rtl_uString *ustrVar, rtl_uString **ustrValue)
439 WCHAR buff[ENV_BUFFER_SIZE];
441 if (GetEnvironmentVariableW(o3tl::toW(ustrVar->buffer), buff, ENV_BUFFER_SIZE) > 0)
443 rtl_uString_newFromStr(ustrValue, o3tl::toU(buff));
444 return osl_Process_E_None;
446 return osl_Process_E_Unknown;
449 oslProcessError SAL_CALL osl_setEnvironment(rtl_uString *ustrVar, rtl_uString *ustrValue)
451 // set Windows environment variable
452 LPCWSTR lpName = o3tl::toW(ustrVar->buffer);
453 LPCWSTR lpValue = o3tl::toW(ustrValue->buffer);
454 if (SetEnvironmentVariableW(lpName, lpValue))
456 auto buffer = std::unique_ptr<wchar_t[]>(
457 new wchar_t[wcslen(lpName) + 1 + wcslen(lpValue) + 1]);
458 wcscpy(buffer.get(), lpName);
459 wcscat(buffer.get(), L"=");
460 wcscat(buffer.get(), lpValue);
461 _wputenv(buffer.get());
462 return osl_Process_E_None;
464 return osl_Process_E_Unknown;
467 oslProcessError SAL_CALL osl_clearEnvironment(rtl_uString *ustrVar)
469 // delete the variable from the current process environment
470 // by setting SetEnvironmentVariable's second parameter to NULL
471 LPCWSTR lpName = o3tl::toW(ustrVar->buffer);
472 if (SetEnvironmentVariableW(lpName, nullptr))
474 auto buffer = std::unique_ptr<wchar_t[]>(
475 new wchar_t[wcslen(lpName) + 1 + 1]);
476 wcscpy(buffer.get(), lpName);
477 wcscat(buffer.get(), L"=");
478 _wputenv(buffer.get());
479 return osl_Process_E_None;
481 return osl_Process_E_Unknown;
484 oslProcessError SAL_CALL osl_getProcessWorkingDir( rtl_uString **pustrWorkingDir )
486 ::osl::LongPathBuffer< sal_Unicode > aBuffer( MAX_LONG_PATH );
487 DWORD dwLen = 0;
489 osl_acquireMutex( g_CurrentDirectoryMutex );
490 dwLen = GetCurrentDirectoryW( aBuffer.getBufSizeInSymbols(), o3tl::toW(aBuffer) );
491 osl_releaseMutex( g_CurrentDirectoryMutex );
493 if ( dwLen && dwLen < aBuffer.getBufSizeInSymbols() )
495 oslFileError eError;
496 rtl_uString *ustrTemp = nullptr;
498 rtl_uString_newFromStr_WithLength( &ustrTemp, aBuffer, dwLen );
499 eError = osl_getFileURLFromSystemPath( ustrTemp, pustrWorkingDir );
501 rtl_uString_release( ustrTemp );
503 if ( osl_File_E_None != eError )
504 return osl_Process_E_Unknown;
505 else
506 return osl_Process_E_None;
508 else
509 return osl_Process_E_Unknown;
512 static rtl_Locale * g_theProcessLocale = nullptr;
514 oslProcessError SAL_CALL osl_getProcessLocale( rtl_Locale ** ppLocale )
516 osl_acquireMutex( *osl_getGlobalMutex() );
518 /* determine the users default locale */
519 if( nullptr == g_theProcessLocale )
520 imp_getProcessLocale( &g_theProcessLocale );
522 /* or return the cached value */
523 *ppLocale = g_theProcessLocale;
525 osl_releaseMutex( *osl_getGlobalMutex() );
526 return osl_Process_E_None;
529 oslProcessError SAL_CALL osl_setProcessLocale( rtl_Locale * pLocale )
531 osl_acquireMutex( *osl_getGlobalMutex() );
533 /* check if locale is supported */
534 if( RTL_TEXTENCODING_DONTKNOW == osl_getTextEncodingFromLocale( pLocale ) )
535 return osl_Process_E_Unknown;
537 /* just remember the locale here */
538 g_theProcessLocale = pLocale;
540 osl_releaseMutex( *osl_getGlobalMutex() );
541 return osl_Process_E_None;
544 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */