Version 4.2.0.1, tag libreoffice-4.2.0.1
[LibreOffice.git] / sal / osl / w32 / procimpl.cxx
blobdcf6af8274e2bf57f833810570179031c0b34ff5
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 .
21 #define UNICODE
22 #define _UNICODE
24 #ifndef WIN32_LEAN_AND_MEAN
25 # define WIN32_LEAN_AND_MEAN
26 # ifdef _MSC_VER
27 # pragma warning(push,1) /* disable warnings within system headers */
28 # endif
29 # include <windows.h>
30 # ifdef _MSC_VER
31 # pragma warning(pop)
32 # endif
33 # include <tchar.h>
34 # undef WIN32_LEAN_AND_MEAN
35 #endif
36 #include "procimpl.h"
37 #include <rtl/ustring.hxx>
38 #include <rtl/ustrbuf.hxx>
39 #include "secimpl.h"
40 #include "rtl/allocator.hxx"
41 #include <osl/file.hxx>
43 #include <list>
44 #include <vector>
45 #include <algorithm>
46 #include <string>
47 #include <string.h>
49 //#################################################
50 extern "C" oslFileHandle SAL_CALL osl_createFileHandleFromOSHandle( HANDLE hFile, sal_uInt32 uFlags );
52 //#################################################
53 const sal_Unicode NAME_VALUE_SEPARATOR = TEXT('=');
54 const sal_Char* SPACE = " ";
55 const rtl::OUString ENV_COMSPEC ("COMSPEC");
56 const rtl::OUString QUOTE("\"");
58 namespace /* private */
60 //#################################################
61 typedef std::list<rtl::OUString> string_container_t;
62 typedef string_container_t::iterator string_container_iterator_t;
63 typedef string_container_t::const_iterator string_container_const_iterator_t;
64 typedef std::pair<string_container_iterator_t, string_container_iterator_t> iterator_pair_t;
65 typedef std::vector<sal_Unicode> environment_container_t;
67 //#################################################
68 /* Function object that compares two strings that are
69 expected to be environment variables in the form
70 "name=value". Only the 'name' part will be compared.
71 The comparison is in upper case and returns true
72 if the first of both strings is less than the
73 second one. */
74 struct less_environment_variable :
75 public std::binary_function<rtl::OUString, rtl::OUString, bool>
77 bool operator() (const rtl::OUString& lhs, const rtl::OUString& rhs) const
79 OSL_ENSURE((lhs.indexOf(NAME_VALUE_SEPARATOR) > -1) && \
80 (rhs.indexOf(NAME_VALUE_SEPARATOR) > -1), \
81 "Malformed environment variable");
83 // Windows compares environment variables uppercase
84 // so we do it, too
85 return (rtl_ustr_compare_WithLength(
86 lhs.toAsciiUpperCase().pData->buffer,
87 lhs.indexOf(NAME_VALUE_SEPARATOR),
88 rhs.toAsciiUpperCase().pData->buffer,
89 rhs.indexOf(NAME_VALUE_SEPARATOR)) < 0);
93 //#################################################
94 /* Function object used by for_each algorithm to
95 calculate the sum of the length of all strings
96 in a string container. */
97 class sum_of_string_lengths
99 public:
100 //--------------------------------
101 sum_of_string_lengths() : sum_(0) {}
103 //--------------------------------
104 void operator() (const rtl::OUString& string)
106 OSL_ASSERT(string.getLength());
108 // always include the terminating '\0'
109 if (string.getLength())
110 sum_ += string.getLength() + 1;
113 //--------------------------------
114 operator size_t () const
116 return sum_;
118 private:
119 size_t sum_;
122 //#################################################
123 inline size_t calc_sum_of_string_lengths(const string_container_t& string_cont)
125 return std::for_each(
126 string_cont.begin(), string_cont.end(), sum_of_string_lengths());
129 //#################################################
130 void read_environment(/*out*/ string_container_t* environment)
132 // GetEnvironmentStrings returns a sorted list, Windows
133 // sorts environment variables upper case
134 LPTSTR env = reinterpret_cast<LPTSTR>(GetEnvironmentStrings());
135 LPTSTR p = env;
137 while (size_t l = _tcslen(p))
139 environment->push_back(reinterpret_cast<const sal_Unicode*>(p));
140 p += l + 1;
142 FreeEnvironmentStrings(env);
145 //#################################################
146 /* the environment list must be sorted, new values
147 should either replace existing ones or should be
148 added to the list, environment variables will
149 be handled case-insensitive */
150 bool create_merged_environment(
151 rtl_uString* env_vars[],
152 sal_uInt32 env_vars_count,
153 /*in|out*/ string_container_t* merged_env)
155 OSL_ASSERT(env_vars && env_vars_count > 0 && merged_env);
157 read_environment(merged_env);
159 for (sal_uInt32 i = 0; i < env_vars_count; i++)
161 rtl::OUString env_var = rtl::OUString(env_vars[i]);
163 if (env_var.getLength() == 0)
164 return false;
166 iterator_pair_t iter_pair = std::equal_range(
167 merged_env->begin(),
168 merged_env->end(),
169 env_var,
170 less_environment_variable());
172 if (env_var.indexOf(NAME_VALUE_SEPARATOR) == -1)
174 merged_env->erase(iter_pair.first, iter_pair.second);
176 else
178 if (iter_pair.first != iter_pair.second) // found
179 *iter_pair.first = env_var;
180 else // not found
181 merged_env->insert(iter_pair.first, env_var);
184 return true;
187 //#################################################
188 /* Create a merged environment */
189 bool setup_process_environment(
190 rtl_uString* environment_vars[],
191 sal_uInt32 n_environment_vars,
192 /*in|out*/ environment_container_t& environment)
194 string_container_t merged_env;
195 if (!create_merged_environment(environment_vars, n_environment_vars, &merged_env))
196 return false;
198 // allocate enough space for the '\0'-separated environment strings and
199 // a final '\0'
200 environment.resize(calc_sum_of_string_lengths(merged_env) + 1);
202 string_container_const_iterator_t iter = merged_env.begin();
203 string_container_const_iterator_t iter_end = merged_env.end();
205 sal_uInt32 pos = 0;
206 for (/**/; iter != iter_end; ++iter)
208 rtl::OUString envv = *iter;
210 OSL_ASSERT(envv.getLength());
212 sal_uInt32 n = envv.getLength() + 1; // copy the final '\0', too
213 memcpy(
214 reinterpret_cast<void*>(&environment[pos]),
215 reinterpret_cast<const void*>(envv.getStr()),
216 n * sizeof(sal_Unicode));
217 pos += n;
219 environment[pos] = 0; // append a final '\0'
221 return true;
224 //##########################################################
225 /* In contrast to the Win32 API function CreatePipe with
226 this function the caller is able to determine separately
227 which handle of the pipe is inheritable. */
228 bool create_pipe(
229 PHANDLE p_read_pipe,
230 bool b_read_pipe_inheritable,
231 PHANDLE p_write_pipe,
232 bool b_write_pipe_inheritable,
233 LPVOID p_security_descriptor = NULL,
234 DWORD pipe_size = 0)
236 SECURITY_ATTRIBUTES sa;
237 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
238 sa.lpSecurityDescriptor = p_security_descriptor;
239 sa.bInheritHandle = b_read_pipe_inheritable || b_write_pipe_inheritable;
241 BOOL bRet = FALSE;
242 HANDLE hTemp = NULL;
244 if (!b_read_pipe_inheritable && b_write_pipe_inheritable)
246 bRet = CreatePipe(&hTemp, p_write_pipe, &sa, pipe_size);
248 if (bRet && !DuplicateHandle(GetCurrentProcess(), hTemp,
249 GetCurrentProcess(), p_read_pipe, 0, FALSE,
250 DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS))
252 CloseHandle(hTemp);
253 CloseHandle(*p_read_pipe);
254 return false;
257 else if (b_read_pipe_inheritable && !b_write_pipe_inheritable)
259 bRet = CreatePipe(p_read_pipe, &hTemp, &sa, pipe_size);
261 if (bRet && !DuplicateHandle(GetCurrentProcess(), hTemp,
262 GetCurrentProcess(), p_write_pipe, 0, FALSE,
263 DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS))
265 CloseHandle(hTemp);
266 CloseHandle(*p_write_pipe);
267 return false;
270 else
272 bRet = CreatePipe(p_read_pipe, p_write_pipe, &sa, pipe_size);
274 return bRet;
277 //#########################################################
278 // Add a quote sign to the start and the end of a string
279 // if not already present
280 rtl::OUString quote_string(const rtl::OUString& string)
282 rtl::OUStringBuffer quoted;
283 if (string.indexOf(QUOTE) != 0)
284 quoted.append(QUOTE);
286 quoted.append(string);
288 if (string.lastIndexOf(QUOTE) != (string.getLength() - 1))
289 quoted.append(QUOTE);
291 return quoted.makeStringAndClear();
294 //The parameter path must be a system path. If it is longer than 260 characters
295 //then it is shortened using the GetShortPathName function. This function only
296 //works if the path exists. Because "path" can be the path to an executable, it
297 //may not have the file extension ".exe". However, if the file on disk has the
298 //".exe" extension, then the function will fail. In this case a second attempt
299 //is started by adding the parameter "extension" to "path".
300 rtl::OUString getShortPath(rtl::OUString const & path, rtl::OUString const & extension)
302 rtl::OUString ret(path);
303 if (path.getLength() > 260)
305 std::vector<sal_Unicode> vec(path.getLength() + 1);
306 //GetShortPathNameW only works if the file can be found!
307 const DWORD len = GetShortPathNameW(
308 reinterpret_cast<LPCWSTR>(path.getStr()), reinterpret_cast<LPWSTR>(&vec[0]), path.getLength() + 1);
310 if (!len && GetLastError() == ERROR_FILE_NOT_FOUND
311 && extension.getLength())
313 const rtl::OUString extPath(path + extension);
314 std::vector<sal_Unicode> vec2(
315 extPath.getLength() + 1);
316 const DWORD len2 = GetShortPathNameW(
317 reinterpret_cast<LPCWSTR>(extPath.getStr()), reinterpret_cast<LPWSTR>(&vec2[0]), extPath.getLength() + 1);
318 ret = rtl::OUString(&vec2[0], len2);
320 else
322 ret = rtl::OUString(&vec[0], len);
325 return ret;
327 //##########################################################
328 // Returns the system path of the executable which can either
329 // be provided via the strImageName parameter or as first
330 // element of the strArguments list.
331 // The returned path will be quoted if it contains spaces.
332 rtl::OUString get_executable_path(
333 rtl_uString* image_name,
334 rtl_uString* cmdline_args[],
335 sal_uInt32 n_cmdline_args,
336 bool search_path)
338 rtl::OUString exe_name;
340 if (image_name)
341 exe_name = image_name;
342 else if (n_cmdline_args)
343 exe_name = rtl::OUString(cmdline_args[0]);
345 rtl::OUString exe_url = exe_name;
346 if (search_path)
347 osl_searchFileURL(exe_name.pData, NULL, &exe_url.pData);
349 rtl::OUString exe_path;
350 if (osl::FileBase::E_None != osl::FileBase::getSystemPathFromFileURL(exe_url, exe_path))
351 return rtl::OUString();
353 exe_path = getShortPath(exe_path, rtl::OUString(".exe"));
355 if (exe_path.indexOf(' ') != -1)
356 exe_path = quote_string(exe_path);
358 return exe_path;
361 //##########################################################
362 rtl::OUString get_file_extension(const rtl::OUString& file_name)
364 sal_Int32 index = file_name.lastIndexOf('.');
365 if ((index != -1) && ((index + 1) < file_name.getLength()))
366 return file_name.copy(index + 1);
368 return rtl::OUString();
371 //##########################################################
372 bool is_batch_file(const rtl::OUString& file_name)
374 rtl::OUString ext = get_file_extension(file_name);
375 return (ext.equalsIgnoreAsciiCaseAsciiL(RTL_CONSTASCII_STRINGPARAM("bat")) ||
376 ext.equalsIgnoreAsciiCaseAsciiL(RTL_CONSTASCII_STRINGPARAM("cmd")) ||
377 ext.equalsIgnoreAsciiCaseAsciiL(RTL_CONSTASCII_STRINGPARAM("btm")));
380 //##########################################################
381 rtl::OUString get_batch_processor()
383 rtl::OUString comspec;
384 osl_getEnvironment(ENV_COMSPEC.pData, &comspec.pData);
386 OSL_ASSERT(comspec.getLength());
388 /* check if comspec path contains blanks and quote it if any */
389 if (comspec.indexOf(' ') != -1)
390 comspec = quote_string(comspec);
392 return comspec;
395 } // namespace private
398 //#################################################
399 oslProcessError SAL_CALL osl_executeProcess(
400 rtl_uString *strImageName,
401 rtl_uString *strArguments[],
402 sal_uInt32 nArguments,
403 oslProcessOption Options,
404 oslSecurity Security,
405 rtl_uString *strDirectory,
406 rtl_uString *strEnvironmentVars[],
407 sal_uInt32 nEnvironmentVars,
408 oslProcess *pProcess
411 return osl_executeProcess_WithRedirectedIO(
412 strImageName,
413 strArguments,
414 nArguments,
415 Options,
416 Security,
417 strDirectory,
418 strEnvironmentVars,
419 nEnvironmentVars,
420 pProcess,
421 NULL, NULL, NULL );
424 //#################################################
425 oslProcessError SAL_CALL osl_executeProcess_WithRedirectedIO(
426 rtl_uString *ustrImageName,
427 rtl_uString *ustrArguments[],
428 sal_uInt32 nArguments,
429 oslProcessOption Options,
430 oslSecurity Security,
431 rtl_uString *ustrDirectory,
432 rtl_uString *ustrEnvironmentVars[],
433 sal_uInt32 nEnvironmentVars,
434 oslProcess *pProcess,
435 oslFileHandle *pProcessInputWrite,
436 oslFileHandle *pProcessOutputRead,
437 oslFileHandle *pProcessErrorRead)
439 rtl::OUString exe_path = get_executable_path(
440 ustrImageName, ustrArguments, nArguments, (Options & osl_Process_SEARCHPATH));
442 if (0 == exe_path.getLength())
443 return osl_Process_E_NotFound;
445 if (pProcess == NULL)
446 return osl_Process_E_InvalidError;
448 DWORD flags = NORMAL_PRIORITY_CLASS;
449 rtl::OUStringBuffer command_line;
451 if (is_batch_file(exe_path))
453 rtl::OUString batch_processor = get_batch_processor();
455 if (batch_processor.getLength())
457 /* cmd.exe does not work without a console window */
458 if (!(Options & osl_Process_WAIT) || (Options & osl_Process_DETACHED))
459 flags |= CREATE_NEW_CONSOLE;
461 command_line.append(batch_processor);
462 command_line.appendAscii(" /c ");
464 else
465 // should we return here in case of error?
466 return osl_Process_E_Unknown;
469 command_line.append(exe_path);
471 /* Add remaining arguments to command line. If ustrImageName is NULL
472 the first parameter is the name of the executable so we have to
473 start at 1 instead of 0 */
474 for (sal_uInt32 n = (NULL != ustrImageName) ? 0 : 1; n < nArguments; n++)
476 command_line.appendAscii(SPACE);
478 /* Quote arguments containing blanks */
479 if (rtl::OUString(ustrArguments[n]).indexOf(' ') != -1)
480 command_line.append(quote_string(ustrArguments[n]));
481 else
482 command_line.append(ustrArguments[n]);
485 environment_container_t environment;
486 LPVOID p_environment = NULL;
488 if (nEnvironmentVars && ustrEnvironmentVars)
490 if (!setup_process_environment(
491 ustrEnvironmentVars, nEnvironmentVars, environment))
492 return osl_Process_E_InvalidError;
494 flags |= CREATE_UNICODE_ENVIRONMENT;
495 p_environment = &environment[0];
498 rtl::OUString cwd;
499 if (ustrDirectory && ustrDirectory->length && (osl::FileBase::E_None != osl::FileBase::getSystemPathFromFileURL(ustrDirectory, cwd)))
500 return osl_Process_E_InvalidError;
502 LPCWSTR p_cwd = (cwd.getLength()) ? reinterpret_cast<LPCWSTR>(cwd.getStr()) : NULL;
504 if ((Options & osl_Process_DETACHED) && !(flags & CREATE_NEW_CONSOLE))
505 flags |= DETACHED_PROCESS;
507 STARTUPINFO startup_info;
508 memset(&startup_info, 0, sizeof(STARTUPINFO));
510 startup_info.cb = sizeof(STARTUPINFO);
511 startup_info.dwFlags = STARTF_USESHOWWINDOW;
512 startup_info.lpDesktop = const_cast<LPWSTR>(L"");
514 /* Create pipes for redirected IO */
515 HANDLE hInputRead = NULL;
516 HANDLE hInputWrite = NULL;
517 if (pProcessInputWrite && create_pipe(&hInputRead, true, &hInputWrite, false))
518 startup_info.hStdInput = hInputRead;
520 HANDLE hOutputRead = NULL;
521 HANDLE hOutputWrite = NULL;
522 if (pProcessOutputRead && create_pipe(&hOutputRead, false, &hOutputWrite, true))
523 startup_info.hStdOutput = hOutputWrite;
525 HANDLE hErrorRead = NULL;
526 HANDLE hErrorWrite = NULL;
527 if (pProcessErrorRead && create_pipe(&hErrorRead, false, &hErrorWrite, true))
528 startup_info.hStdError = hErrorWrite;
530 bool b_inherit_handles = false;
531 if (pProcessInputWrite || pProcessOutputRead || pProcessErrorRead)
533 startup_info.dwFlags |= STARTF_USESTDHANDLES;
534 b_inherit_handles = true;
537 switch(Options & (osl_Process_NORMAL | osl_Process_HIDDEN | osl_Process_MINIMIZED | osl_Process_MAXIMIZED | osl_Process_FULLSCREEN))
539 case osl_Process_HIDDEN:
540 startup_info.wShowWindow = SW_HIDE;
541 flags |= CREATE_NO_WINDOW; // ignored for non-console
542 // applications; ignored on
543 // Win9x
544 break;
546 case osl_Process_MINIMIZED:
547 startup_info.wShowWindow = SW_MINIMIZE;
548 break;
550 case osl_Process_MAXIMIZED:
551 case osl_Process_FULLSCREEN:
552 startup_info.wShowWindow = SW_MAXIMIZE;
553 break;
555 default:
556 startup_info.wShowWindow = SW_NORMAL;
559 rtl::OUString cmdline = command_line.makeStringAndClear();
560 PROCESS_INFORMATION process_info;
561 BOOL bRet = FALSE;
563 if ((Security != NULL) && (((oslSecurityImpl*)Security)->m_hToken != NULL))
565 bRet = CreateProcessAsUser(
566 ((oslSecurityImpl*)Security)->m_hToken,
567 NULL, const_cast<LPTSTR>(reinterpret_cast<LPCTSTR>(cmdline.getStr())), NULL, NULL,
568 b_inherit_handles, flags, p_environment, p_cwd,
569 &startup_info, &process_info);
571 else
573 bRet = CreateProcess(
574 NULL, const_cast<LPTSTR>(reinterpret_cast<LPCTSTR>(cmdline.getStr())), NULL, NULL,
575 b_inherit_handles, flags, p_environment, p_cwd,
576 &startup_info, &process_info);
579 /* Now we can close the pipe ends that are used by the child process */
581 if (hInputRead)
582 CloseHandle(hInputRead);
584 if (hOutputWrite)
585 CloseHandle(hOutputWrite);
587 if (hErrorWrite)
588 CloseHandle(hErrorWrite);
590 if (bRet)
592 CloseHandle(process_info.hThread);
594 oslProcessImpl* pProcImpl = reinterpret_cast<oslProcessImpl*>(
595 rtl_allocateMemory(sizeof(oslProcessImpl)));
597 if (pProcImpl != NULL)
599 pProcImpl->m_hProcess = process_info.hProcess;
600 pProcImpl->m_IdProcess = process_info.dwProcessId;
602 *pProcess = (oslProcess)pProcImpl;
604 if (Options & osl_Process_WAIT)
605 WaitForSingleObject(pProcImpl->m_hProcess, INFINITE);
607 if (pProcessInputWrite)
608 *pProcessInputWrite = osl_createFileHandleFromOSHandle(hInputWrite, osl_File_OpenFlag_Write);
610 if (pProcessOutputRead)
611 *pProcessOutputRead = osl_createFileHandleFromOSHandle(hOutputRead, osl_File_OpenFlag_Read);
613 if (pProcessErrorRead)
614 *pProcessErrorRead = osl_createFileHandleFromOSHandle(hErrorRead, osl_File_OpenFlag_Read);
616 return osl_Process_E_None;
620 /* if an error occurred we have to close the server side pipe ends too */
622 if (hInputWrite)
623 CloseHandle(hInputWrite);
625 if (hOutputRead)
626 CloseHandle(hOutputRead);
628 if (hErrorRead)
629 CloseHandle(hErrorRead);
631 return osl_Process_E_Unknown;
634 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */