diff options
Diffstat (limited to 'sal/osl/w32/procimpl.cxx')
-rw-r--r-- | sal/osl/w32/procimpl.cxx | 603 |
1 files changed, 603 insertions, 0 deletions
diff --git a/sal/osl/w32/procimpl.cxx b/sal/osl/w32/procimpl.cxx new file mode 100644 index 000000000000..379caeb5affc --- /dev/null +++ b/sal/osl/w32/procimpl.cxx @@ -0,0 +1,603 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: procimpl.cxx,v $ + * $Revision: 1.10 $ + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sal.hxx" + +#define UNICODE +#define _UNICODE + +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# ifdef _MSC_VER +# pragma warning(push,1) /* disable warnings within system headers */ +# endif +# include <windows.h> +# ifdef _MSC_VER +# pragma warning(pop) +# endif +# include <tchar.h> +# undef WIN32_LEAN_AND_MEAN +#endif +#include "procimpl.h" +#include <rtl/ustring.hxx> +#include <rtl/ustrbuf.hxx> +#include "secimpl.h" +#include "rtl/allocator.hxx" +#include <osl/file.hxx> + +#include <list> +#include <vector> +#include <algorithm> +#include <string> + +//################################################# +extern "C" oslFileHandle SAL_CALL osl_createFileHandleFromOSHandle( HANDLE hFile, sal_uInt32 uFlags ); + +//################################################# +const sal_Unicode NAME_VALUE_SEPARATOR = TEXT('='); +const sal_Char* SPACE = " "; +const rtl::OUString ENV_COMSPEC = rtl::OUString::createFromAscii("COMSPEC"); +const rtl::OUString QUOTE = rtl::OUString::createFromAscii("\""); + +namespace /* private */ +{ + //################################################# + typedef std::list<rtl::OUString, rtl::Allocator<rtl::OUString> > string_container_t; + typedef string_container_t::iterator string_container_iterator_t; + typedef string_container_t::const_iterator string_container_const_iterator_t; + typedef std::pair<string_container_iterator_t, string_container_iterator_t> iterator_pair_t; + typedef std::vector<sal_Unicode, rtl::Allocator<sal_Unicode> > environment_container_t; + + //################################################# + /* Function object that compares two strings that are + expected to be environment variables in the form + "name=value". Only the 'name' part will be compared. + The comparison is in upper case and returns true + if the first of both strings is less than the + second one. */ + struct less_environment_variable : + public std::binary_function<rtl::OUString, rtl::OUString, bool> + { + bool operator() (const rtl::OUString& lhs, const rtl::OUString& rhs) const + { + OSL_ENSURE((lhs.indexOf(NAME_VALUE_SEPARATOR) > -1) && \ + (rhs.indexOf(NAME_VALUE_SEPARATOR) > -1), \ + "Malformed environment variable"); + + // Windows compares environment variables uppercase + // so we do it, too + return (rtl_ustr_compare_WithLength( + lhs.toAsciiUpperCase().pData->buffer, + lhs.indexOf(NAME_VALUE_SEPARATOR), + rhs.toAsciiUpperCase().pData->buffer, + rhs.indexOf(NAME_VALUE_SEPARATOR)) < 0); + } + }; + + //################################################# + /* Function object used by for_each algorithm to + calculate the sum of the length of all strings + in a string container. */ + class sum_of_string_lengths + { + public: + //-------------------------------- + sum_of_string_lengths() : sum_(0) {} + + //-------------------------------- + void operator() (const rtl::OUString& string) + { + OSL_ASSERT(string.getLength()); + + // always include the terminating '\0' + if (string.getLength()) + sum_ += string.getLength() + 1; + } + + //-------------------------------- + operator size_t () const + { + return sum_; + } + private: + size_t sum_; + }; + + //################################################# + inline size_t calc_sum_of_string_lengths(const string_container_t& string_cont) + { + return std::for_each( + string_cont.begin(), string_cont.end(), sum_of_string_lengths()); + } + + //################################################# + void read_environment(/*out*/ string_container_t* environment) + { + // GetEnvironmentStrings returns a sorted list, Windows + // sorts environment variables upper case + LPTSTR env = reinterpret_cast<LPTSTR>(GetEnvironmentStrings()); + LPTSTR p = env; + + while (size_t l = _tcslen(p)) + { + environment->push_back(reinterpret_cast<const sal_Unicode*>(p)); + p += l + 1; + } + FreeEnvironmentStrings(env); + } + + //################################################# + /* the environment list must be sorted, new values + should either replace existing ones or should be + added to the list, environment variables will + be handled case-insensitive */ + bool create_merged_environment( + rtl_uString* env_vars[], + sal_uInt32 env_vars_count, + /*in|out*/ string_container_t* merged_env) + { + OSL_ASSERT(env_vars && env_vars_count > 0 && merged_env); + + read_environment(merged_env); + + for (sal_uInt32 i = 0; i < env_vars_count; i++) + { + rtl::OUString env_var = rtl::OUString(env_vars[i]); + + if ((env_var.getLength() == 0) || + (env_var.indexOf(NAME_VALUE_SEPARATOR) == -1)) + return false; + + iterator_pair_t iter_pair = std::equal_range( + merged_env->begin(), + merged_env->end(), + env_var, + less_environment_variable()); + + if (iter_pair.first != iter_pair.second) // found + *iter_pair.first = env_var; + else // not found + merged_env->insert(iter_pair.first, env_var); + } + return true; + } + + //################################################# + /* Create a merged environment */ + bool setup_process_environment( + rtl_uString* environment_vars[], + sal_uInt32 n_environment_vars, + /*in|out*/ environment_container_t& environment) + { + string_container_t merged_env; + if (!create_merged_environment(environment_vars, n_environment_vars, &merged_env)) + return false; + + // reserve enough space for the '\0'-separated environment strings and + // a final '\0' + environment.reserve(calc_sum_of_string_lengths(merged_env) + 1); + + string_container_const_iterator_t iter = merged_env.begin(); + string_container_const_iterator_t iter_end = merged_env.end(); + + sal_uInt32 pos = 0; + for (/**/; iter != iter_end; ++iter) + { + rtl::OUString envv = *iter; + + OSL_ASSERT(envv.getLength()); + + sal_uInt32 n = envv.getLength() + 1; // copy the final '\0', too + rtl_copyMemory( + reinterpret_cast<void*>(&environment[pos]), + reinterpret_cast<const void*>(envv.getStr()), + n * sizeof(sal_Unicode)); + pos += n; + } + environment[pos] = 0; // append a final '\0' + + return true; + } + + //########################################################## + /* In contrast to the Win32 API function CreatePipe with + this function the caller is able to determine separately + which handle of the pipe is inheritable. */ + bool create_pipe( + PHANDLE p_read_pipe, + bool b_read_pipe_inheritable, + PHANDLE p_write_pipe, + bool b_write_pipe_inheritable, + LPVOID p_security_descriptor = NULL, + DWORD pipe_size = 0) + { + SECURITY_ATTRIBUTES sa; + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.lpSecurityDescriptor = p_security_descriptor; + sa.bInheritHandle = b_read_pipe_inheritable || b_write_pipe_inheritable; + + BOOL bRet = FALSE; + HANDLE hTemp = NULL; + + if (!b_read_pipe_inheritable && b_write_pipe_inheritable) + { + bRet = CreatePipe(&hTemp, p_write_pipe, &sa, pipe_size); + + if (bRet && !DuplicateHandle(GetCurrentProcess(), hTemp, + GetCurrentProcess(), p_read_pipe, 0, FALSE, + DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) + { + CloseHandle(hTemp); + CloseHandle(*p_read_pipe); + return false; + } + } + else if (b_read_pipe_inheritable && !b_write_pipe_inheritable) + { + bRet = CreatePipe(p_read_pipe, &hTemp, &sa, pipe_size); + + if (bRet && !DuplicateHandle(GetCurrentProcess(), hTemp, + GetCurrentProcess(), p_write_pipe, 0, FALSE, + DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) + { + CloseHandle(hTemp); + CloseHandle(*p_write_pipe); + return false; + } + } + else + { + bRet = CreatePipe(p_read_pipe, p_write_pipe, &sa, pipe_size); + } + return bRet; + } + + //######################################################### + // Add a quote sign to the start and the end of a string + // if not already present + rtl::OUString quote_string(const rtl::OUString& string) + { + rtl::OUStringBuffer quoted; + if (string.indexOf(QUOTE) != 0) + quoted.append(QUOTE); + + quoted.append(string); + + if (string.lastIndexOf(QUOTE) != (string.getLength() - 1)) + quoted.append(QUOTE); + + return quoted.makeStringAndClear(); + } + + //########################################################## + // Returns the system path of the executable which can either + // be provided via the strImageName parameter or as first + // element of the strArguments list. + // The returned path will be quoted if it contains spaces. + rtl::OUString get_executable_path( + rtl_uString* image_name, + rtl_uString* cmdline_args[], + sal_uInt32 n_cmdline_args, + bool search_path) + { + rtl::OUString exe_name; + + if (image_name) + exe_name = image_name; + else if (n_cmdline_args) + exe_name = rtl::OUString(cmdline_args[0]); + + rtl::OUString exe_url = exe_name; + if (search_path) + osl_searchFileURL(exe_name.pData, NULL, &exe_url.pData); + + rtl::OUString exe_path; + if (osl_File_E_None != osl::FileBase::getSystemPathFromFileURL(exe_url, exe_path)) + return rtl::OUString(); + + if (exe_path.indexOf(' ') != -1) + exe_path = quote_string(exe_path); + + return exe_path; + } + + //########################################################## + rtl::OUString get_file_extension(const rtl::OUString& file_name) + { + sal_Int32 index = file_name.lastIndexOf('.'); + if ((index != -1) && ((index + 1) < file_name.getLength())) + return file_name.copy(index + 1); + + return rtl::OUString(); + } + + //########################################################## + bool is_batch_file(const rtl::OUString& file_name) + { + rtl::OUString ext = get_file_extension(file_name); + return (ext.equalsIgnoreAsciiCaseAscii("bat") || + ext.equalsIgnoreAsciiCaseAscii("cmd") || + ext.equalsIgnoreAsciiCaseAscii("btm")); + } + + //########################################################## + rtl::OUString get_batch_processor() + { + rtl::OUString comspec; + osl_getEnvironment(ENV_COMSPEC.pData, &comspec.pData); + + OSL_ASSERT(comspec.getLength()); + + /* check if comspec path contains blanks and quote it if any */ + if (comspec.indexOf(' ') != -1) + comspec = quote_string(comspec); + + return comspec; + } + +} // namespace private + + +//################################################# +oslProcessError SAL_CALL osl_executeProcess( + rtl_uString *strImageName, + rtl_uString *strArguments[], + sal_uInt32 nArguments, + oslProcessOption Options, + oslSecurity Security, + rtl_uString *strDirectory, + rtl_uString *strEnvironmentVars[], + sal_uInt32 nEnvironmentVars, + oslProcess *pProcess +) +{ + return osl_executeProcess_WithRedirectedIO( + strImageName, + strArguments, + nArguments, + Options, + Security, + strDirectory, + strEnvironmentVars, + nEnvironmentVars, + pProcess, + NULL, NULL, NULL ); +} + +//################################################# +oslProcessError SAL_CALL osl_executeProcess_WithRedirectedIO( + rtl_uString *ustrImageName, + rtl_uString *ustrArguments[], + sal_uInt32 nArguments, + oslProcessOption Options, + oslSecurity Security, + rtl_uString *ustrDirectory, + rtl_uString *ustrEnvironmentVars[], + sal_uInt32 nEnvironmentVars, + oslProcess *pProcess, + oslFileHandle *pProcessInputWrite, + oslFileHandle *pProcessOutputRead, + oslFileHandle *pProcessErrorRead) +{ + rtl::OUString exe_path = get_executable_path( + ustrImageName, ustrArguments, nArguments, (Options & osl_Process_SEARCHPATH)); + + if (0 == exe_path.getLength()) + return osl_Process_E_NotFound; + + if (pProcess == NULL) + return osl_Process_E_InvalidError; + + DWORD flags = NORMAL_PRIORITY_CLASS; + rtl::OUStringBuffer command_line; + + if (is_batch_file(exe_path)) + { + rtl::OUString batch_processor = get_batch_processor(); + + if (batch_processor.getLength()) + { + /* cmd.exe does not work without a console window */ + if (!(Options & osl_Process_WAIT) || (Options & osl_Process_DETACHED)) + flags |= CREATE_NEW_CONSOLE; + + command_line.append(batch_processor); + command_line.appendAscii(" /c "); + } + else + // should we return here in case of error? + return osl_Process_E_Unknown; + } + + command_line.append(exe_path); + + /* Add remaining arguments to command line. If ustrImageName is NULL + the first parameter is the name of the executable so we have to + start at 1 instead of 0 */ + for (sal_uInt32 n = (NULL != ustrImageName) ? 0 : 1; n < nArguments; n++) + { + command_line.appendAscii(SPACE); + + /* Quote arguments containing blanks */ + if (rtl::OUString(ustrArguments[n]).indexOf(' ') != -1) + command_line.append(quote_string(ustrArguments[n])); + else + command_line.append(ustrArguments[n]); + } + + environment_container_t environment; + LPVOID p_environment = NULL; + + if (nEnvironmentVars && ustrEnvironmentVars) + { + if (!setup_process_environment( + ustrEnvironmentVars, nEnvironmentVars, environment)) + return osl_Process_E_InvalidError; + + flags |= CREATE_UNICODE_ENVIRONMENT; + p_environment = &environment[0]; + } + + rtl::OUString cwd; + if (ustrDirectory && ustrDirectory->length && (osl_File_E_None != osl::FileBase::getSystemPathFromFileURL(ustrDirectory, cwd))) + return osl_Process_E_InvalidError; + + LPCWSTR p_cwd = (cwd.getLength()) ? reinterpret_cast<LPCWSTR>(cwd.getStr()) : NULL; + + if ((Options & osl_Process_DETACHED) && !(flags & CREATE_NEW_CONSOLE)) + flags |= DETACHED_PROCESS; + + STARTUPINFO startup_info; + memset(&startup_info, 0, sizeof(STARTUPINFO)); + + startup_info.cb = sizeof(STARTUPINFO); + startup_info.dwFlags = STARTF_USESHOWWINDOW; + startup_info.lpDesktop = L""; + + /* Create pipes for redirected IO */ + HANDLE hInputRead = NULL; + HANDLE hInputWrite = NULL; + if (pProcessInputWrite && create_pipe(&hInputRead, true, &hInputWrite, false)) + startup_info.hStdInput = hInputRead; + + HANDLE hOutputRead = NULL; + HANDLE hOutputWrite = NULL; + if (pProcessOutputRead && create_pipe(&hOutputRead, false, &hOutputWrite, true)) + startup_info.hStdOutput = hOutputWrite; + + HANDLE hErrorRead = NULL; + HANDLE hErrorWrite = NULL; + if (pProcessErrorRead && create_pipe(&hErrorRead, false, &hErrorWrite, true)) + startup_info.hStdError = hErrorWrite; + + bool b_inherit_handles = false; + if (pProcessInputWrite || pProcessOutputRead || pProcessErrorRead) + { + startup_info.dwFlags |= STARTF_USESTDHANDLES; + b_inherit_handles = true; + } + + switch(Options & (osl_Process_NORMAL | osl_Process_HIDDEN | osl_Process_MINIMIZED | osl_Process_MAXIMIZED | osl_Process_FULLSCREEN)) + { + case osl_Process_HIDDEN: + startup_info.wShowWindow = SW_HIDE; + flags |= CREATE_NO_WINDOW; // ignored for non-console + // applications; ignored on + // Win9x + break; + + case osl_Process_MINIMIZED: + startup_info.wShowWindow = SW_MINIMIZE; + break; + + case osl_Process_MAXIMIZED: + case osl_Process_FULLSCREEN: + startup_info.wShowWindow = SW_MAXIMIZE; + break; + + default: + startup_info.wShowWindow = SW_NORMAL; + } + + rtl::OUString cmdline = command_line.makeStringAndClear(); + PROCESS_INFORMATION process_info; + BOOL bRet = FALSE; + + if ((Security != NULL) && (((oslSecurityImpl*)Security)->m_hToken != NULL)) + { + bRet = CreateProcessAsUser( + ((oslSecurityImpl*)Security)->m_hToken, + NULL, const_cast<LPTSTR>(reinterpret_cast<LPCTSTR>(cmdline.getStr())), NULL, NULL, + b_inherit_handles, flags, p_environment, p_cwd, + &startup_info, &process_info); + } + else + { + bRet = CreateProcess( + NULL, const_cast<LPTSTR>(reinterpret_cast<LPCTSTR>(cmdline.getStr())), NULL, NULL, + b_inherit_handles, flags, p_environment, p_cwd, + &startup_info, &process_info); + } + + /* Now we can close the pipe ends that are used by the child process */ + + if (hInputRead) + CloseHandle(hInputRead); + + if (hOutputWrite) + CloseHandle(hOutputWrite); + + if (hErrorWrite) + CloseHandle(hErrorWrite); + + if (bRet) + { + CloseHandle(process_info.hThread); + + oslProcessImpl* pProcImpl = reinterpret_cast<oslProcessImpl*>( + rtl_allocateMemory(sizeof(oslProcessImpl))); + + if (pProcImpl != NULL) + { + pProcImpl->m_hProcess = process_info.hProcess; + pProcImpl->m_IdProcess = process_info.dwProcessId; + + *pProcess = (oslProcess)pProcImpl; + + if (Options & osl_Process_WAIT) + WaitForSingleObject(pProcImpl->m_hProcess, INFINITE); + + if (pProcessInputWrite) + *pProcessInputWrite = osl_createFileHandleFromOSHandle(hInputWrite, osl_File_OpenFlag_Write); + + if (pProcessOutputRead) + *pProcessOutputRead = osl_createFileHandleFromOSHandle(hOutputRead, osl_File_OpenFlag_Read); + + if (pProcessErrorRead) + *pProcessErrorRead = osl_createFileHandleFromOSHandle(hErrorRead, osl_File_OpenFlag_Read); + + return osl_Process_E_None; + } + } + + /* if an error occured we have to close the server side pipe ends too */ + + if (hInputWrite) + CloseHandle(hInputWrite); + + if (hOutputRead) + CloseHandle(hOutputRead); + + if (hErrorRead) + CloseHandle(hErrorRead); + + return osl_Process_E_Unknown; +} |