bump product version to 6.1.0.2
[LibreOffice.git] / sal / osl / w32 / procimpl.cxx
blob967fbd359d62a4f3e0cc82e6cb3b6a8d47cd79ab
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/ustring.hxx>
29 #include <rtl/ustrbuf.hxx>
30 #include "secimpl.hxx"
31 #include <osl/file.hxx>
32 #include <o3tl/char16_t2wchar_t.hxx>
34 #include <vector>
35 #include <algorithm>
37 namespace /* private */
39 typedef std::vector<rtl::OUString> string_container_t;
40 typedef string_container_t::iterator string_container_iterator_t;
41 typedef string_container_t::const_iterator string_container_const_iterator_t;
42 typedef std::vector<sal_Unicode> environment_container_t;
44 /* Function object that compares two strings that are
45 expected to be environment variables in the form
46 "name=value". Only the 'name' part will be compared.
47 The comparison is in upper case and returns true
48 if the first of both strings is less than the
49 second one. */
50 struct less_environment_variable
52 bool operator() (const rtl::OUString& lhs, const rtl::OUString& rhs) const
54 OSL_ENSURE((lhs.indexOf(L'=') > -1) &&
55 (rhs.indexOf(L'=') > -1),
56 "Malformed environment variable");
58 // Windows compares environment variables uppercase
59 // so we do it, too
60 return (rtl_ustr_compare_WithLength(
61 lhs.toAsciiUpperCase().pData->buffer,
62 lhs.indexOf(L'='),
63 rhs.toAsciiUpperCase().pData->buffer,
64 rhs.indexOf(L'=')) < 0);
68 /* Function object used by for_each algorithm to
69 calculate the sum of the length of all strings
70 in a string container. */
71 class sum_of_string_lengths
73 public:
75 sum_of_string_lengths() : sum_(0) {}
77 void operator() (const rtl::OUString& string)
79 OSL_ASSERT(string.getLength());
81 // always include the terminating '\0'
82 if (string.getLength())
83 sum_ += string.getLength() + 1;
86 operator size_t () const
88 return sum_;
90 private:
91 size_t sum_;
94 inline size_t calc_sum_of_string_lengths(const string_container_t& string_cont)
96 return std::for_each(
97 string_cont.begin(), string_cont.end(), sum_of_string_lengths());
100 void read_environment(/*out*/ string_container_t* environment)
102 // GetEnvironmentStrings returns a sorted list, Windows
103 // sorts environment variables upper case
104 LPWSTR env = GetEnvironmentStringsW();
105 LPWSTR p = env;
107 while (size_t l = wcslen(p))
109 environment->push_back(o3tl::toU(p));
110 p += l + 1;
112 FreeEnvironmentStringsW(env);
114 // it is apparently possible that the environment is not completely
115 // sorted; Cygwin may append entries, which breaks the equal_range
116 std::stable_sort(environment->begin(), environment->end(),
117 less_environment_variable());
120 /* the environment list must be sorted, new values
121 should either replace existing ones or should be
122 added to the list, environment variables will
123 be handled case-insensitive */
124 bool create_merged_environment(
125 rtl_uString* env_vars[],
126 sal_uInt32 env_vars_count,
127 /*in|out*/ string_container_t* merged_env)
129 OSL_ASSERT(env_vars && env_vars_count > 0 && merged_env);
131 read_environment(merged_env);
133 for (sal_uInt32 i = 0; i < env_vars_count; i++)
135 rtl::OUString env_var = rtl::OUString(env_vars[i]);
137 if (env_var.getLength() == 0)
138 return false;
140 auto iter_pair = std::equal_range(
141 merged_env->begin(),
142 merged_env->end(),
143 env_var,
144 less_environment_variable());
146 if (env_var.indexOf(L'=') == -1)
148 merged_env->erase(iter_pair.first, iter_pair.second);
150 else
152 if (iter_pair.first != iter_pair.second) // found
153 *iter_pair.first = env_var;
154 else // not found
155 merged_env->insert(iter_pair.first, env_var);
158 return true;
161 /* Create a merged environment */
162 bool setup_process_environment(
163 rtl_uString* environment_vars[],
164 sal_uInt32 n_environment_vars,
165 /*in|out*/ environment_container_t& environment)
167 string_container_t merged_env;
168 if (!create_merged_environment(environment_vars, n_environment_vars, &merged_env))
169 return false;
171 // allocate enough space for the '\0'-separated environment strings and
172 // a final '\0'
173 environment.resize(calc_sum_of_string_lengths(merged_env) + 1);
175 string_container_const_iterator_t iter = merged_env.begin();
176 string_container_const_iterator_t iter_end = merged_env.end();
178 sal_uInt32 pos = 0;
179 for (/**/; iter != iter_end; ++iter)
181 rtl::OUString envv = *iter;
183 OSL_ASSERT(envv.getLength());
185 sal_uInt32 n = envv.getLength() + 1; // copy the final '\0', too
186 memcpy(&environment[pos], envv.getStr(), n * sizeof(sal_Unicode));
187 pos += n;
189 environment[pos] = 0; // append a final '\0'
191 return true;
194 /* In contrast to the Win32 API function CreatePipe with
195 this function the caller is able to determine separately
196 which handle of the pipe is inheritable. */
197 bool create_pipe(
198 PHANDLE p_read_pipe,
199 bool b_read_pipe_inheritable,
200 PHANDLE p_write_pipe,
201 bool b_write_pipe_inheritable,
202 LPVOID p_security_descriptor = nullptr,
203 DWORD pipe_size = 0)
205 SECURITY_ATTRIBUTES sa;
206 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
207 sa.lpSecurityDescriptor = p_security_descriptor;
208 sa.bInheritHandle = b_read_pipe_inheritable || b_write_pipe_inheritable;
210 BOOL bRet = FALSE;
211 HANDLE hTemp = nullptr;
213 if (!b_read_pipe_inheritable && b_write_pipe_inheritable)
215 bRet = CreatePipe(&hTemp, p_write_pipe, &sa, pipe_size);
217 if (bRet && !DuplicateHandle(GetCurrentProcess(), hTemp,
218 GetCurrentProcess(), p_read_pipe, 0, FALSE,
219 DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS))
221 CloseHandle(hTemp);
222 CloseHandle(*p_read_pipe);
223 return false;
226 else if (b_read_pipe_inheritable && !b_write_pipe_inheritable)
228 bRet = CreatePipe(p_read_pipe, &hTemp, &sa, pipe_size);
230 if (bRet && !DuplicateHandle(GetCurrentProcess(), hTemp,
231 GetCurrentProcess(), p_write_pipe, 0, FALSE,
232 DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS))
234 CloseHandle(hTemp);
235 CloseHandle(*p_write_pipe);
236 return false;
239 else
241 bRet = CreatePipe(p_read_pipe, p_write_pipe, &sa, pipe_size);
243 return bRet;
246 // Add a quote sign to the start and the end of a string
247 // if not already present
248 rtl::OUString quote_string(const rtl::OUString& string)
250 rtl::OUStringBuffer quoted;
251 if (string.indexOf(L'"') != 0)
252 quoted.append('"');
254 quoted.append(string);
256 if (string.lastIndexOf(L'"') != (string.getLength() - 1))
257 quoted.append('"');
259 return quoted.makeStringAndClear();
262 // The parameter path must be a system path. If it is longer than 260 characters
263 // then it is shortened using the GetShortPathName function. This function only
264 // works if the path exists. Because "path" can be the path to an executable, it
265 // may not have the file extension ".exe". However, if the file on disk has the
266 // ".exe" extension, then the function will fail. In this case a second attempt
267 // is started by adding the parameter "extension" to "path".
268 rtl::OUString getShortPath(rtl::OUString const & path, rtl::OUString const & extension)
270 rtl::OUString ret(path);
271 if (path.getLength() > 260)
273 std::vector<sal_Unicode> vec(path.getLength() + 1);
274 //GetShortPathNameW only works if the file can be found!
275 const DWORD len = GetShortPathNameW(
276 o3tl::toW(path.getStr()), o3tl::toW(&vec[0]), path.getLength() + 1);
278 if (!len && GetLastError() == ERROR_FILE_NOT_FOUND
279 && extension.getLength())
281 const rtl::OUString extPath(path + extension);
282 std::vector<sal_Unicode> vec2(
283 extPath.getLength() + 1);
284 const DWORD len2 = GetShortPathNameW(
285 o3tl::toW(extPath.getStr()), o3tl::toW(&vec2[0]), extPath.getLength() + 1);
286 ret = rtl::OUString(&vec2[0], len2);
288 else
290 ret = rtl::OUString(&vec[0], len);
293 return ret;
296 // Returns the system path of the executable which can either
297 // be provided via the strImageName parameter or as first
298 // element of the strArguments list.
299 // The returned path will be quoted if it contains spaces.
300 rtl::OUString get_executable_path(
301 rtl_uString* image_name,
302 rtl_uString* cmdline_args[],
303 sal_uInt32 n_cmdline_args,
304 bool search_path)
306 rtl::OUString exe_name;
308 if (image_name)
309 exe_name = image_name;
310 else if (n_cmdline_args)
311 exe_name = rtl::OUString(cmdline_args[0]);
313 rtl::OUString exe_url = exe_name;
314 if (search_path)
315 osl_searchFileURL(exe_name.pData, nullptr, &exe_url.pData);
317 rtl::OUString exe_path;
318 if (osl::FileBase::E_None != osl::FileBase::getSystemPathFromFileURL(exe_url, exe_path))
319 return rtl::OUString();
321 exe_path = getShortPath(exe_path, ".exe");
323 if (exe_path.indexOf(' ') != -1)
324 exe_path = quote_string(exe_path);
326 return exe_path;
329 rtl::OUString get_file_extension(const rtl::OUString& file_name)
331 sal_Int32 index = file_name.lastIndexOf('.');
332 if ((index != -1) && ((index + 1) < file_name.getLength()))
333 return file_name.copy(index + 1);
335 return rtl::OUString();
338 bool is_batch_file(const rtl::OUString& file_name)
340 rtl::OUString ext = get_file_extension(file_name);
341 return (ext.equalsIgnoreAsciiCase("bat") ||
342 ext.equalsIgnoreAsciiCase("cmd") ||
343 ext.equalsIgnoreAsciiCase("btm"));
346 const rtl::OUString ENV_COMSPEC ("COMSPEC");
347 rtl::OUString get_batch_processor()
349 rtl::OUString comspec;
350 osl_getEnvironment(ENV_COMSPEC.pData, &comspec.pData);
352 OSL_ASSERT(comspec.getLength());
354 /* check if comspec path contains blanks and quote it if any */
355 if (comspec.indexOf(' ') != -1)
356 comspec = quote_string(comspec);
358 return comspec;
361 } // namespace private
363 oslProcessError SAL_CALL osl_executeProcess(
364 rtl_uString *strImageName,
365 rtl_uString *strArguments[],
366 sal_uInt32 nArguments,
367 oslProcessOption Options,
368 oslSecurity Security,
369 rtl_uString *strDirectory,
370 rtl_uString *strEnvironmentVars[],
371 sal_uInt32 nEnvironmentVars,
372 oslProcess *pProcess
375 return osl_executeProcess_WithRedirectedIO(
376 strImageName,
377 strArguments,
378 nArguments,
379 Options,
380 Security,
381 strDirectory,
382 strEnvironmentVars,
383 nEnvironmentVars,
384 pProcess,
385 nullptr, nullptr, nullptr );
388 oslProcessError SAL_CALL osl_executeProcess_WithRedirectedIO(
389 rtl_uString *ustrImageName,
390 rtl_uString *ustrArguments[],
391 sal_uInt32 nArguments,
392 oslProcessOption Options,
393 oslSecurity Security,
394 rtl_uString *ustrDirectory,
395 rtl_uString *ustrEnvironmentVars[],
396 sal_uInt32 nEnvironmentVars,
397 oslProcess *pProcess,
398 oslFileHandle *pProcessInputWrite,
399 oslFileHandle *pProcessOutputRead,
400 oslFileHandle *pProcessErrorRead)
402 rtl::OUString exe_path = get_executable_path(
403 ustrImageName, ustrArguments, nArguments, (Options & osl_Process_SEARCHPATH) != 0);
405 if (0 == exe_path.getLength())
406 return osl_Process_E_NotFound;
408 if (pProcess == nullptr)
409 return osl_Process_E_InvalidError;
411 DWORD flags = NORMAL_PRIORITY_CLASS;
412 rtl::OUStringBuffer command_line;
414 if (is_batch_file(exe_path))
416 rtl::OUString batch_processor = get_batch_processor();
418 if (batch_processor.getLength())
420 /* cmd.exe does not work without a console window */
421 if (!(Options & osl_Process_WAIT) || (Options & osl_Process_DETACHED))
422 flags |= CREATE_NEW_CONSOLE;
424 command_line.append(batch_processor);
425 command_line.append(" /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 (rtl::OUString(ustrArguments[n]).indexOf(' ') != -1)
443 command_line.append(quote_string(ustrArguments[n]));
444 else
445 command_line.append(ustrArguments[n]);
448 environment_container_t 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[0];
461 rtl::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 memset(&startup_info, 0, sizeof(startup_info));
473 startup_info.cb = sizeof(startup_info);
474 startup_info.dwFlags = STARTF_USESHOWWINDOW;
475 startup_info.lpDesktop = const_cast<LPWSTR>(L"");
477 /* Create pipes for redirected IO */
478 HANDLE hInputRead = nullptr;
479 HANDLE hInputWrite = nullptr;
480 if (pProcessInputWrite && create_pipe(&hInputRead, true, &hInputWrite, false))
481 startup_info.hStdInput = hInputRead;
483 HANDLE hOutputRead = nullptr;
484 HANDLE hOutputWrite = nullptr;
485 if (pProcessOutputRead && create_pipe(&hOutputRead, false, &hOutputWrite, true))
486 startup_info.hStdOutput = hOutputWrite;
488 HANDLE hErrorRead = nullptr;
489 HANDLE hErrorWrite = nullptr;
490 if (pProcessErrorRead && create_pipe(&hErrorRead, false, &hErrorWrite, true))
491 startup_info.hStdError = hErrorWrite;
493 bool b_inherit_handles = false;
494 if (pProcessInputWrite || pProcessOutputRead || pProcessErrorRead)
496 startup_info.dwFlags |= STARTF_USESTDHANDLES;
497 b_inherit_handles = true;
500 switch(Options & (osl_Process_NORMAL | osl_Process_HIDDEN | osl_Process_MINIMIZED | osl_Process_MAXIMIZED | osl_Process_FULLSCREEN))
502 case osl_Process_HIDDEN:
503 startup_info.wShowWindow = SW_HIDE;
504 flags |= CREATE_NO_WINDOW; // ignored for non-console
505 // applications; ignored on
506 // Win9x
507 break;
509 case osl_Process_MINIMIZED:
510 startup_info.wShowWindow = SW_MINIMIZE;
511 break;
513 case osl_Process_MAXIMIZED:
514 case osl_Process_FULLSCREEN:
515 startup_info.wShowWindow = SW_MAXIMIZE;
516 break;
518 default:
519 startup_info.wShowWindow = SW_NORMAL;
522 rtl::OUString cmdline = command_line.makeStringAndClear();
523 PROCESS_INFORMATION process_info;
524 BOOL bRet = FALSE;
526 if ((Security != nullptr) && (static_cast<oslSecurityImpl*>(Security)->m_hToken != nullptr))
528 bRet = CreateProcessAsUserW(
529 static_cast<oslSecurityImpl*>(Security)->m_hToken,
530 nullptr, const_cast<LPWSTR>(o3tl::toW(cmdline.getStr())), nullptr, nullptr,
531 b_inherit_handles, flags, p_environment, p_cwd,
532 &startup_info, &process_info);
534 else
536 bRet = CreateProcessW(
537 nullptr, const_cast<LPWSTR>(o3tl::toW(cmdline.getStr())), nullptr, nullptr,
538 b_inherit_handles, flags, p_environment, p_cwd,
539 &startup_info, &process_info);
542 /* Now we can close the pipe ends that are used by the child process */
544 if (hInputRead)
545 CloseHandle(hInputRead);
547 if (hOutputWrite)
548 CloseHandle(hOutputWrite);
550 if (hErrorWrite)
551 CloseHandle(hErrorWrite);
553 if (bRet)
555 CloseHandle(process_info.hThread);
557 oslProcessImpl* pProcImpl = static_cast<oslProcessImpl*>(
558 rtl_allocateMemory(sizeof(oslProcessImpl)));
560 if (pProcImpl != nullptr)
562 pProcImpl->m_hProcess = process_info.hProcess;
563 pProcImpl->m_IdProcess = process_info.dwProcessId;
565 *pProcess = static_cast<oslProcess>(pProcImpl);
567 if (Options & osl_Process_WAIT)
568 WaitForSingleObject(pProcImpl->m_hProcess, INFINITE);
570 if (pProcessInputWrite)
571 *pProcessInputWrite = osl_createFileHandleFromOSHandle(hInputWrite, osl_File_OpenFlag_Write);
573 if (pProcessOutputRead)
574 *pProcessOutputRead = osl_createFileHandleFromOSHandle(hOutputRead, osl_File_OpenFlag_Read);
576 if (pProcessErrorRead)
577 *pProcessErrorRead = osl_createFileHandleFromOSHandle(hErrorRead, osl_File_OpenFlag_Read);
579 return osl_Process_E_None;
583 /* if an error occurred we have to close the server side pipe ends too */
585 if (hInputWrite)
586 CloseHandle(hInputWrite);
588 if (hOutputRead)
589 CloseHandle(hOutputRead);
591 if (hErrorRead)
592 CloseHandle(hErrorRead);
594 return osl_Process_E_Unknown;
597 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */