Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sal / osl / w32 / procimpl.cxx
blob1689d6ac6be99d2bfb2b0f1727df9ff699df544d
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 #ifndef WIN32_LEAN_AND_MEAN
21 # define WIN32_LEAN_AND_MEAN
22 # include <windows.h>
23 # undef WIN32_LEAN_AND_MEAN
24 #endif
26 #include "file-impl.hxx"
27 #include "procimpl.hxx"
28 #include <rtl/alloc.h>
29 #include <rtl/ustring.hxx>
30 #include <rtl/ustrbuf.hxx>
31 #include "secimpl.hxx"
32 #include <osl/file.hxx>
33 #include <o3tl/char16_t2wchar_t.hxx>
35 #include <vector>
36 #include <algorithm>
38 namespace /* private */
40 /* Function object that compares two strings that are
41 expected to be environment variables in the form
42 "name=value". Only the 'name' part will be compared.
43 The comparison is in upper case and returns true
44 if the first of both strings is less than the
45 second one. */
46 struct less_environment_variable
48 bool operator() (const OUString& lhs, const OUString& rhs) const
50 OSL_ENSURE((lhs.indexOf(L'=') > -1) &&
51 (rhs.indexOf(L'=') > -1),
52 "Malformed environment variable");
54 // Windows compares environment variables uppercase
55 // so we do it, too
56 return (rtl_ustr_compare_WithLength(
57 lhs.toAsciiUpperCase().pData->buffer,
58 lhs.indexOf(L'='),
59 rhs.toAsciiUpperCase().pData->buffer,
60 rhs.indexOf(L'=')) < 0);
64 /* Function object used by for_each algorithm to
65 calculate the sum of the length of all strings
66 in a string container. */
67 class sum_of_string_lengths
69 public:
71 sum_of_string_lengths() : sum_(0) {}
73 void operator() (const OUString& string)
75 OSL_ASSERT(string.getLength());
77 // always include the terminating '\0'
78 if (string.getLength())
79 sum_ += string.getLength() + 1;
82 operator size_t () const
84 return sum_;
86 private:
87 size_t sum_;
90 size_t calc_sum_of_string_lengths(const std::vector<OUString>& string_cont)
92 return std::for_each(
93 string_cont.begin(), string_cont.end(), sum_of_string_lengths());
96 void read_environment(/*out*/ std::vector<OUString>* environment)
98 // GetEnvironmentStrings returns a sorted list, Windows
99 // sorts environment variables upper case
100 LPWSTR env = GetEnvironmentStringsW();
101 LPWSTR p = env;
103 while (size_t l = wcslen(p))
105 environment->push_back(OUString(o3tl::toU(p)));
106 p += l + 1;
108 FreeEnvironmentStringsW(env);
110 // it is apparently possible that the environment is not completely
111 // sorted; Cygwin may append entries, which breaks the equal_range
112 std::stable_sort(environment->begin(), environment->end(),
113 less_environment_variable());
116 /* the environment list must be sorted, new values
117 should either replace existing ones or should be
118 added to the list, environment variables will
119 be handled case-insensitive */
120 bool create_merged_environment(
121 rtl_uString* env_vars[],
122 sal_uInt32 env_vars_count,
123 /*in|out*/ std::vector<OUString>* merged_env)
125 OSL_ASSERT(env_vars && env_vars_count > 0 && merged_env);
127 read_environment(merged_env);
129 for (sal_uInt32 i = 0; i < env_vars_count; i++)
131 OUString env_var(env_vars[i]);
133 if (env_var.getLength() == 0)
134 return false;
136 auto iter_pair = std::equal_range(
137 merged_env->begin(),
138 merged_env->end(),
139 env_var,
140 less_environment_variable());
142 if (env_var.indexOf(L'=') == -1)
144 merged_env->erase(iter_pair.first, iter_pair.second);
146 else
148 if (iter_pair.first != iter_pair.second) // found
149 *iter_pair.first = env_var;
150 else // not found
151 merged_env->insert(iter_pair.first, env_var);
154 return true;
157 /* Create a merged environment */
158 bool setup_process_environment(
159 rtl_uString* environment_vars[],
160 sal_uInt32 n_environment_vars,
161 /*in|out*/ std::vector<sal_Unicode>& environment)
163 std::vector<OUString> merged_env;
164 if (!create_merged_environment(environment_vars, n_environment_vars, &merged_env))
165 return false;
167 // allocate enough space for the '\0'-separated environment strings and
168 // a final '\0'
169 environment.resize(calc_sum_of_string_lengths(merged_env) + 1);
171 sal_uInt32 pos = 0;
172 for (auto& envv : merged_env)
174 OSL_ASSERT(envv.getLength());
176 sal_uInt32 n = envv.getLength() + 1; // copy the final '\0', too
177 memcpy(&environment[pos], envv.getStr(), n * sizeof(sal_Unicode));
178 pos += n;
180 environment[pos] = 0; // append a final '\0'
182 return true;
185 /* In contrast to the Win32 API function CreatePipe with
186 this function the caller is able to determine separately
187 which handle of the pipe is inheritable. */
188 bool create_pipe(
189 PHANDLE p_read_pipe,
190 bool b_read_pipe_inheritable,
191 PHANDLE p_write_pipe,
192 bool b_write_pipe_inheritable,
193 LPVOID p_security_descriptor = nullptr,
194 DWORD pipe_size = 0)
196 SECURITY_ATTRIBUTES sa;
197 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
198 sa.lpSecurityDescriptor = p_security_descriptor;
199 sa.bInheritHandle = b_read_pipe_inheritable || b_write_pipe_inheritable;
201 bool bRet = false;
202 HANDLE hTemp = nullptr;
204 if (!b_read_pipe_inheritable && b_write_pipe_inheritable)
206 bRet = CreatePipe(&hTemp, p_write_pipe, &sa, pipe_size);
208 if (bRet && !DuplicateHandle(GetCurrentProcess(), hTemp,
209 GetCurrentProcess(), p_read_pipe, 0, FALSE,
210 DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS))
212 CloseHandle(hTemp);
213 CloseHandle(*p_read_pipe);
214 return false;
217 else if (b_read_pipe_inheritable && !b_write_pipe_inheritable)
219 bRet = CreatePipe(p_read_pipe, &hTemp, &sa, pipe_size);
221 if (bRet && !DuplicateHandle(GetCurrentProcess(), hTemp,
222 GetCurrentProcess(), p_write_pipe, 0, FALSE,
223 DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS))
225 CloseHandle(hTemp);
226 CloseHandle(*p_write_pipe);
227 return false;
230 else
232 bRet = CreatePipe(p_read_pipe, p_write_pipe, &sa, pipe_size);
234 return bRet;
237 // Add a quote sign to the start and the end of a string
238 // if not already present
239 OUString quote_string(const OUString& string)
241 OUStringBuffer quoted;
242 if (string.indexOf(L'"') != 0)
243 quoted.append('"');
245 quoted.append(string);
247 if (string.lastIndexOf(L'"') != (string.getLength() - 1))
248 quoted.append('"');
250 return quoted.makeStringAndClear();
253 // The parameter path must be a system path. If it is longer than 260 characters
254 // then it is shortened using the GetShortPathName function. This function only
255 // works if the path exists. Because "path" can be the path to an executable, it
256 // may not have the file extension ".exe". However, if the file on disk has the
257 // ".exe" extension, then the function will fail. In this case a second attempt
258 // is started by adding the parameter "extension" to "path".
259 OUString getShortPath(OUString const & path, OUString const & extension)
261 OUString ret(path);
262 if (path.getLength() > 260)
264 std::vector<sal_Unicode> vec(path.getLength() + 1);
265 //GetShortPathNameW only works if the file can be found!
266 const DWORD len = GetShortPathNameW(
267 o3tl::toW(path.getStr()), o3tl::toW(vec.data()), path.getLength() + 1);
269 if (!len && GetLastError() == ERROR_FILE_NOT_FOUND
270 && extension.getLength())
272 const OUString extPath(path + extension);
273 std::vector<sal_Unicode> vec2(
274 extPath.getLength() + 1);
275 const DWORD len2 = GetShortPathNameW(
276 o3tl::toW(extPath.getStr()), o3tl::toW(vec2.data()), extPath.getLength() + 1);
277 ret = OUString(vec2.data(), len2);
279 else
281 ret = OUString(vec.data(), len);
284 return ret;
287 // Returns the system path of the executable which can either
288 // be provided via the strImageName parameter or as first
289 // element of the strArguments list.
290 // The returned path will be quoted if it contains spaces.
291 OUString get_executable_path(
292 rtl_uString* image_name,
293 rtl_uString* cmdline_args[],
294 sal_uInt32 n_cmdline_args,
295 bool search_path)
297 OUString exe_name;
299 if (image_name)
300 exe_name = image_name;
301 else if (n_cmdline_args)
302 exe_name = OUString(cmdline_args[0]);
304 OUString exe_url = exe_name;
305 if (search_path)
306 osl_searchFileURL(exe_name.pData, nullptr, &exe_url.pData);
308 OUString exe_path;
309 if (osl::FileBase::E_None != osl::FileBase::getSystemPathFromFileURL(exe_url, exe_path))
310 return OUString();
312 exe_path = getShortPath(exe_path, ".exe");
314 if (exe_path.indexOf(' ') != -1)
315 exe_path = quote_string(exe_path);
317 return exe_path;
320 OUString get_file_extension(const OUString& file_name)
322 // Quoted file name
323 if ((file_name.indexOf(L'"') == 0) && (file_name.lastIndexOf(L'"') == (file_name.getLength() - 1)))
325 sal_Int32 index = file_name.lastIndexOf('.');
326 if ((index != -1) && ((index + 2) < file_name.getLength()))
327 return file_name.copy(index + 1, file_name.getLength() - (index + 2));
329 // Unquoted file name
330 else
332 sal_Int32 index = file_name.lastIndexOf('.');
333 if ((index != -1) && ((index + 1) < file_name.getLength()))
334 return file_name.copy(index + 1);
336 return OUString();
339 bool is_batch_file(const OUString& file_name)
341 OUString ext = get_file_extension(file_name);
342 return (ext.equalsIgnoreAsciiCase("bat") ||
343 ext.equalsIgnoreAsciiCase("cmd") ||
344 ext.equalsIgnoreAsciiCase("btm"));
347 const OUString ENV_COMSPEC ("COMSPEC");
348 OUString get_batch_processor()
350 OUString comspec;
351 osl_getEnvironment(ENV_COMSPEC.pData, &comspec.pData);
353 OSL_ASSERT(comspec.getLength());
355 /* check if comspec path contains blanks and quote it if any */
356 if (comspec.indexOf(' ') != -1)
357 comspec = quote_string(comspec);
359 return comspec;
362 } // namespace private
364 oslProcessError SAL_CALL osl_executeProcess(
365 rtl_uString *strImageName,
366 rtl_uString *strArguments[],
367 sal_uInt32 nArguments,
368 oslProcessOption Options,
369 oslSecurity Security,
370 rtl_uString *strDirectory,
371 rtl_uString *strEnvironmentVars[],
372 sal_uInt32 nEnvironmentVars,
373 oslProcess *pProcess
376 return osl_executeProcess_WithRedirectedIO(
377 strImageName,
378 strArguments,
379 nArguments,
380 Options,
381 Security,
382 strDirectory,
383 strEnvironmentVars,
384 nEnvironmentVars,
385 pProcess,
386 nullptr, nullptr, nullptr );
389 oslProcessError SAL_CALL osl_executeProcess_WithRedirectedIO(
390 rtl_uString *ustrImageName,
391 rtl_uString *ustrArguments[],
392 sal_uInt32 nArguments,
393 oslProcessOption Options,
394 oslSecurity Security,
395 rtl_uString *ustrDirectory,
396 rtl_uString *ustrEnvironmentVars[],
397 sal_uInt32 nEnvironmentVars,
398 oslProcess *pProcess,
399 oslFileHandle *pProcessInputWrite,
400 oslFileHandle *pProcessOutputRead,
401 oslFileHandle *pProcessErrorRead)
403 OUString exe_path = get_executable_path(
404 ustrImageName, ustrArguments, nArguments, (Options & osl_Process_SEARCHPATH) != 0);
406 if (0 == exe_path.getLength())
407 return osl_Process_E_NotFound;
409 if (pProcess == nullptr)
410 return osl_Process_E_InvalidError;
412 DWORD flags = NORMAL_PRIORITY_CLASS;
413 OUStringBuffer command_line;
415 if (is_batch_file(exe_path))
417 OUString batch_processor = get_batch_processor();
419 if (batch_processor.getLength())
421 /* cmd.exe does not work without a console window */
422 if (!(Options & osl_Process_WAIT) || (Options & osl_Process_DETACHED))
423 flags |= CREATE_NEW_CONSOLE;
425 command_line.append(batch_processor + " /c ");
427 else
428 // should we return here in case of error?
429 return osl_Process_E_Unknown;
432 command_line.append(exe_path);
434 /* Add remaining arguments to command line. If ustrImageName is nullptr
435 the first parameter is the name of the executable so we have to
436 start at 1 instead of 0 */
437 for (sal_uInt32 n = (nullptr != ustrImageName) ? 0 : 1; n < nArguments; n++)
439 command_line.append(" ");
441 /* Quote arguments containing blanks */
442 if (OUString(ustrArguments[n]).indexOf(' ') != -1)
443 command_line.append(quote_string(ustrArguments[n]));
444 else
445 command_line.append(ustrArguments[n]);
448 std::vector<sal_Unicode> environment;
449 LPVOID p_environment = nullptr;
451 if (nEnvironmentVars && ustrEnvironmentVars)
453 if (!setup_process_environment(
454 ustrEnvironmentVars, nEnvironmentVars, environment))
455 return osl_Process_E_InvalidError;
457 flags |= CREATE_UNICODE_ENVIRONMENT;
458 p_environment = environment.data();
461 OUString cwd;
462 if (ustrDirectory && ustrDirectory->length && (osl::FileBase::E_None != osl::FileBase::getSystemPathFromFileURL(ustrDirectory, cwd)))
463 return osl_Process_E_InvalidError;
465 LPCWSTR p_cwd = (cwd.getLength()) ? o3tl::toW(cwd.getStr()) : nullptr;
467 if ((Options & osl_Process_DETACHED) && !(flags & CREATE_NEW_CONSOLE))
468 flags |= DETACHED_PROCESS;
470 STARTUPINFOW startup_info = {};
471 startup_info.cb = sizeof(startup_info);
472 startup_info.dwFlags = STARTF_USESHOWWINDOW;
473 startup_info.lpDesktop = const_cast<LPWSTR>(L"");
475 /* Create pipes for redirected IO */
476 HANDLE hInputRead = nullptr;
477 HANDLE hInputWrite = nullptr;
478 if (pProcessInputWrite && create_pipe(&hInputRead, true, &hInputWrite, false))
479 startup_info.hStdInput = hInputRead;
481 HANDLE hOutputRead = nullptr;
482 HANDLE hOutputWrite = nullptr;
483 if (pProcessOutputRead && create_pipe(&hOutputRead, false, &hOutputWrite, true))
484 startup_info.hStdOutput = hOutputWrite;
486 HANDLE hErrorRead = nullptr;
487 HANDLE hErrorWrite = nullptr;
488 if (pProcessErrorRead && create_pipe(&hErrorRead, false, &hErrorWrite, true))
489 startup_info.hStdError = hErrorWrite;
491 bool b_inherit_handles = false;
492 if (pProcessInputWrite || pProcessOutputRead || pProcessErrorRead)
494 startup_info.dwFlags |= STARTF_USESTDHANDLES;
495 b_inherit_handles = true;
498 switch(Options & (osl_Process_NORMAL | osl_Process_HIDDEN | osl_Process_MINIMIZED | osl_Process_MAXIMIZED | osl_Process_FULLSCREEN))
500 case osl_Process_HIDDEN:
501 startup_info.wShowWindow = SW_HIDE;
502 flags |= CREATE_NO_WINDOW; // ignored for non-console
503 // applications; ignored on
504 // Win9x
505 break;
507 case osl_Process_MINIMIZED:
508 startup_info.wShowWindow = SW_MINIMIZE;
509 break;
511 case osl_Process_MAXIMIZED:
512 case osl_Process_FULLSCREEN:
513 startup_info.wShowWindow = SW_MAXIMIZE;
514 break;
516 default:
517 startup_info.wShowWindow = SW_NORMAL;
520 OUString cmdline = command_line.makeStringAndClear();
521 PROCESS_INFORMATION process_info;
522 bool bRet = false;
524 if ((Security != nullptr) && (static_cast<oslSecurityImpl*>(Security)->m_hToken != nullptr))
526 bRet = CreateProcessAsUserW(
527 static_cast<oslSecurityImpl*>(Security)->m_hToken,
528 nullptr, const_cast<LPWSTR>(o3tl::toW(cmdline.getStr())), nullptr, nullptr,
529 b_inherit_handles, flags, p_environment, p_cwd,
530 &startup_info, &process_info);
532 else
534 bRet = CreateProcessW(
535 nullptr, const_cast<LPWSTR>(o3tl::toW(cmdline.getStr())), nullptr, nullptr,
536 b_inherit_handles, flags, p_environment, p_cwd,
537 &startup_info, &process_info);
540 /* Now we can close the pipe ends that are used by the child process */
542 if (hInputRead)
543 CloseHandle(hInputRead);
545 if (hOutputWrite)
546 CloseHandle(hOutputWrite);
548 if (hErrorWrite)
549 CloseHandle(hErrorWrite);
551 if (bRet)
553 CloseHandle(process_info.hThread);
555 oslProcessImpl* pProcImpl = static_cast<oslProcessImpl*>(
556 malloc(sizeof(oslProcessImpl)));
558 if (pProcImpl != nullptr)
560 pProcImpl->m_hProcess = process_info.hProcess;
561 pProcImpl->m_IdProcess = process_info.dwProcessId;
563 *pProcess = static_cast<oslProcess>(pProcImpl);
565 if (Options & osl_Process_WAIT)
566 WaitForSingleObject(pProcImpl->m_hProcess, INFINITE);
568 if (pProcessInputWrite)
569 *pProcessInputWrite = osl_createFileHandleFromOSHandle(hInputWrite, osl_File_OpenFlag_Write);
571 if (pProcessOutputRead)
572 *pProcessOutputRead = osl_createFileHandleFromOSHandle(hOutputRead, osl_File_OpenFlag_Read);
574 if (pProcessErrorRead)
575 *pProcessErrorRead = osl_createFileHandleFromOSHandle(hErrorRead, osl_File_OpenFlag_Read);
577 return osl_Process_E_None;
581 /* if an error occurred we have to close the server side pipe ends too */
583 if (hInputWrite)
584 CloseHandle(hInputWrite);
586 if (hOutputRead)
587 CloseHandle(hOutputRead);
589 if (hErrorRead)
590 CloseHandle(hErrorRead);
592 return osl_Process_E_Unknown;
595 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */