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 .
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)
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
82 hProcess
= hDupProcess
; // so we were able to duplicate the handle, all good...
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
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
117 &dwTID
); /* new remote thread's identifier */
121 bool bHasExited
= false;
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
130 // need to close this duplicated process handle...
132 CloseHandle(hProcess
);
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
));
151 pProcImpl
= static_cast< oslProcessImpl
*>( malloc(sizeof(oslProcessImpl
)) );
152 pProcImpl
->m_hProcess
= hProcess
;
153 pProcImpl
->m_IdProcess
= Ident
;
161 void SAL_CALL
osl_freeProcessHandle(oslProcess Process
)
163 if (Process
!= nullptr)
165 CloseHandle(static_cast<oslProcessImpl
*>(Process
)->m_hProcess
);
171 oslProcessError SAL_CALL
osl_getProcessInfo(oslProcess Process
, oslProcessData Fields
,
172 oslProcessInfo
* pInfo
)
177 if (Process
== nullptr)
179 hProcess
= GetCurrentProcess();
180 IdProcess
= GetCurrentProcessId();
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
;
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)
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
))
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
;
262 if (nullptr == Process
)
263 return osl_Process_E_Unknown
;
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
;
280 oslProcessError
bootstrap_getExecutableFile(rtl_uString
** ppFileURL
)
282 oslProcessError result
= osl_Process_E_NotFound
;
284 ::osl::LongPathBuffer
< sal_Unicode
> aBuffer( MAX_LONG_PATH
);
287 if ((buflen
= GetModuleFileNameW (nullptr, o3tl::toW(aBuffer
), aBuffer
.getBufSizeInSymbols())) > 0)
289 rtl_uString
* pAbsPath
= nullptr;
290 rtl_uString_newFromStr_WithLength (&pAbsPath
, aBuffer
, buflen
);
293 /* Convert from path to url. */
294 if (osl_getFileURLFromSystemPath (pAbsPath
, ppFileURL
) == osl_File_E_None
)
297 result
= osl_Process_E_None
;
299 rtl_uString_release (pAbsPath
);
306 struct CommandArgs_Impl
309 rtl_uString
** m_ppArgs
;
314 static struct CommandArgs_Impl g_command_args
=
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)
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
);
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
);
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());
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;
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
;
411 void SAL_CALL
osl_setCommandArgs (int argc
, char ** argv
)
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() )
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
;
489 return osl_Process_E_None
;
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: */