1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 // MARKER(update_precomp.py): autogen include statement, do not remove 29 #include "precompiled_sal.hxx" 30 31 #define UNICODE 32 #define _UNICODE 33 34 #ifndef WIN32_LEAN_AND_MEAN 35 # define WIN32_LEAN_AND_MEAN 36 # ifdef _MSC_VER 37 # pragma warning(push,1) /* disable warnings within system headers */ 38 # endif 39 # include <windows.h> 40 # ifdef _MSC_VER 41 # pragma warning(pop) 42 # endif 43 # include <tchar.h> 44 # undef WIN32_LEAN_AND_MEAN 45 #endif 46 #include "procimpl.h" 47 #include <rtl/ustring.hxx> 48 #include <rtl/ustrbuf.hxx> 49 #include "secimpl.h" 50 #include "rtl/allocator.hxx" 51 #include <osl/file.hxx> 52 53 #include <list> 54 #include <vector> 55 #include <algorithm> 56 #include <string> 57 58 //################################################# 59 extern "C" oslFileHandle SAL_CALL osl_createFileHandleFromOSHandle( HANDLE hFile, sal_uInt32 uFlags ); 60 61 //################################################# 62 const sal_Unicode NAME_VALUE_SEPARATOR = TEXT('='); 63 const sal_Char* SPACE = " "; 64 const rtl::OUString ENV_COMSPEC = rtl::OUString::createFromAscii("COMSPEC"); 65 const rtl::OUString QUOTE = rtl::OUString::createFromAscii("\""); 66 67 namespace /* private */ 68 { 69 //################################################# 70 typedef std::list<rtl::OUString, rtl::Allocator<rtl::OUString> > string_container_t; 71 typedef string_container_t::iterator string_container_iterator_t; 72 typedef string_container_t::const_iterator string_container_const_iterator_t; 73 typedef std::pair<string_container_iterator_t, string_container_iterator_t> iterator_pair_t; 74 typedef std::vector<sal_Unicode, rtl::Allocator<sal_Unicode> > environment_container_t; 75 76 //################################################# 77 /* Function object that compares two strings that are 78 expected to be environment variables in the form 79 "name=value". Only the 'name' part will be compared. 80 The comparison is in upper case and returns true 81 if the first of both strings is less than the 82 second one. */ 83 struct less_environment_variable : 84 public std::binary_function<rtl::OUString, rtl::OUString, bool> 85 { 86 bool operator() (const rtl::OUString& lhs, const rtl::OUString& rhs) const 87 { 88 OSL_ENSURE((lhs.indexOf(NAME_VALUE_SEPARATOR) > -1) && \ 89 (rhs.indexOf(NAME_VALUE_SEPARATOR) > -1), \ 90 "Malformed environment variable"); 91 92 // Windows compares environment variables uppercase 93 // so we do it, too 94 return (rtl_ustr_compare_WithLength( 95 lhs.toAsciiUpperCase().pData->buffer, 96 lhs.indexOf(NAME_VALUE_SEPARATOR), 97 rhs.toAsciiUpperCase().pData->buffer, 98 rhs.indexOf(NAME_VALUE_SEPARATOR)) < 0); 99 } 100 }; 101 102 //################################################# 103 /* Function object used by for_each algorithm to 104 calculate the sum of the length of all strings 105 in a string container. */ 106 class sum_of_string_lengths 107 { 108 public: 109 //-------------------------------- 110 sum_of_string_lengths() : sum_(0) {} 111 112 //-------------------------------- 113 void operator() (const rtl::OUString& string) 114 { 115 OSL_ASSERT(string.getLength()); 116 117 // always include the terminating '\0' 118 if (string.getLength()) 119 sum_ += string.getLength() + 1; 120 } 121 122 //-------------------------------- 123 operator size_t () const 124 { 125 return sum_; 126 } 127 private: 128 size_t sum_; 129 }; 130 131 //################################################# 132 inline size_t calc_sum_of_string_lengths(const string_container_t& string_cont) 133 { 134 return std::for_each( 135 string_cont.begin(), string_cont.end(), sum_of_string_lengths()); 136 } 137 138 //################################################# 139 void read_environment(/*out*/ string_container_t* environment) 140 { 141 // GetEnvironmentStrings returns a sorted list, Windows 142 // sorts environment variables upper case 143 LPTSTR env = reinterpret_cast<LPTSTR>(GetEnvironmentStrings()); 144 LPTSTR p = env; 145 146 while (size_t l = _tcslen(p)) 147 { 148 environment->push_back(reinterpret_cast<const sal_Unicode*>(p)); 149 p += l + 1; 150 } 151 FreeEnvironmentStrings(env); 152 } 153 154 //################################################# 155 /* the environment list must be sorted, new values 156 should either replace existing ones or should be 157 added to the list, environment variables will 158 be handled case-insensitive */ 159 bool create_merged_environment( 160 rtl_uString* env_vars[], 161 sal_uInt32 env_vars_count, 162 /*in|out*/ string_container_t* merged_env) 163 { 164 OSL_ASSERT(env_vars && env_vars_count > 0 && merged_env); 165 166 read_environment(merged_env); 167 168 for (sal_uInt32 i = 0; i < env_vars_count; i++) 169 { 170 rtl::OUString env_var = rtl::OUString(env_vars[i]); 171 172 if (env_var.getLength() == 0) 173 return false; 174 175 iterator_pair_t iter_pair = std::equal_range( 176 merged_env->begin(), 177 merged_env->end(), 178 env_var, 179 less_environment_variable()); 180 181 if (env_var.indexOf(NAME_VALUE_SEPARATOR) == -1) 182 { 183 merged_env->erase(iter_pair.first, iter_pair.second); 184 } 185 else 186 { 187 if (iter_pair.first != iter_pair.second) // found 188 *iter_pair.first = env_var; 189 else // not found 190 merged_env->insert(iter_pair.first, env_var); 191 } 192 } 193 return true; 194 } 195 196 //################################################# 197 /* Create a merged environment */ 198 bool setup_process_environment( 199 rtl_uString* environment_vars[], 200 sal_uInt32 n_environment_vars, 201 /*in|out*/ environment_container_t& environment) 202 { 203 string_container_t merged_env; 204 if (!create_merged_environment(environment_vars, n_environment_vars, &merged_env)) 205 return false; 206 207 // allocate enough space for the '\0'-separated environment strings and 208 // a final '\0' 209 environment.resize(calc_sum_of_string_lengths(merged_env) + 1); 210 211 string_container_const_iterator_t iter = merged_env.begin(); 212 string_container_const_iterator_t iter_end = merged_env.end(); 213 214 sal_uInt32 pos = 0; 215 for (/**/; iter != iter_end; ++iter) 216 { 217 rtl::OUString envv = *iter; 218 219 OSL_ASSERT(envv.getLength()); 220 221 sal_uInt32 n = envv.getLength() + 1; // copy the final '\0', too 222 rtl_copyMemory( 223 reinterpret_cast<void*>(&environment[pos]), 224 reinterpret_cast<const void*>(envv.getStr()), 225 n * sizeof(sal_Unicode)); 226 pos += n; 227 } 228 environment[pos] = 0; // append a final '\0' 229 230 return true; 231 } 232 233 //########################################################## 234 /* In contrast to the Win32 API function CreatePipe with 235 this function the caller is able to determine separately 236 which handle of the pipe is inheritable. */ 237 bool create_pipe( 238 PHANDLE p_read_pipe, 239 bool b_read_pipe_inheritable, 240 PHANDLE p_write_pipe, 241 bool b_write_pipe_inheritable, 242 LPVOID p_security_descriptor = NULL, 243 DWORD pipe_size = 0) 244 { 245 SECURITY_ATTRIBUTES sa; 246 sa.nLength = sizeof(SECURITY_ATTRIBUTES); 247 sa.lpSecurityDescriptor = p_security_descriptor; 248 sa.bInheritHandle = b_read_pipe_inheritable || b_write_pipe_inheritable; 249 250 BOOL bRet = FALSE; 251 HANDLE hTemp = NULL; 252 253 if (!b_read_pipe_inheritable && b_write_pipe_inheritable) 254 { 255 bRet = CreatePipe(&hTemp, p_write_pipe, &sa, pipe_size); 256 257 if (bRet && !DuplicateHandle(GetCurrentProcess(), hTemp, 258 GetCurrentProcess(), p_read_pipe, 0, FALSE, 259 DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) 260 { 261 CloseHandle(hTemp); 262 CloseHandle(*p_read_pipe); 263 return false; 264 } 265 } 266 else if (b_read_pipe_inheritable && !b_write_pipe_inheritable) 267 { 268 bRet = CreatePipe(p_read_pipe, &hTemp, &sa, pipe_size); 269 270 if (bRet && !DuplicateHandle(GetCurrentProcess(), hTemp, 271 GetCurrentProcess(), p_write_pipe, 0, FALSE, 272 DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) 273 { 274 CloseHandle(hTemp); 275 CloseHandle(*p_write_pipe); 276 return false; 277 } 278 } 279 else 280 { 281 bRet = CreatePipe(p_read_pipe, p_write_pipe, &sa, pipe_size); 282 } 283 return bRet; 284 } 285 286 //######################################################### 287 // Add a quote sign to the start and the end of a string 288 // if not already present 289 rtl::OUString quote_string(const rtl::OUString& string) 290 { 291 rtl::OUStringBuffer quoted; 292 if (string.indexOf(QUOTE) != 0) 293 quoted.append(QUOTE); 294 295 quoted.append(string); 296 297 if (string.lastIndexOf(QUOTE) != (string.getLength() - 1)) 298 quoted.append(QUOTE); 299 300 return quoted.makeStringAndClear(); 301 } 302 303 //The parameter path must be a system path. If it is longer than 260 characters 304 //then it is shortened using the GetShortPathName function. This function only 305 //works if the path exists. Because "path" can be the path to an executable, it 306 //may not have the file extension ".exe". However, if the file on disk has the 307 //".exe" extension, then the function will fail. In this case a second attempt 308 //is started by adding the parameter "extension" to "path". 309 rtl::OUString getShortPath(rtl::OUString const & path, rtl::OUString const & extension) 310 { 311 rtl::OUString ret(path); 312 if (path.getLength() > 260) 313 { 314 std::vector<sal_Unicode, rtl::Allocator<sal_Unicode> > vec(path.getLength() + 1); 315 //GetShortPathNameW only works if the file can be found! 316 const DWORD len = GetShortPathNameW( 317 reinterpret_cast<LPCWSTR>(path.getStr()), reinterpret_cast<LPWSTR>(&vec[0]), path.getLength() + 1); 318 319 if (!len && GetLastError() == ERROR_FILE_NOT_FOUND 320 && extension.getLength()) 321 { 322 const rtl::OUString extPath(path + extension); 323 std::vector<sal_Unicode, rtl::Allocator<sal_Unicode> > vec2( 324 extPath.getLength() + 1); 325 const DWORD len2 = GetShortPathNameW( 326 reinterpret_cast<LPCWSTR>(extPath.getStr()), reinterpret_cast<LPWSTR>(&vec2[0]), extPath.getLength() + 1); 327 ret = rtl::OUString(&vec2[0], len2); 328 } 329 else 330 { 331 ret = rtl::OUString(&vec[0], len); 332 } 333 } 334 return ret; 335 } 336 //########################################################## 337 // Returns the system path of the executable which can either 338 // be provided via the strImageName parameter or as first 339 // element of the strArguments list. 340 // The returned path will be quoted if it contains spaces. 341 rtl::OUString get_executable_path( 342 rtl_uString* image_name, 343 rtl_uString* cmdline_args[], 344 sal_uInt32 n_cmdline_args, 345 bool search_path) 346 { 347 rtl::OUString exe_name; 348 349 if (image_name) 350 exe_name = image_name; 351 else if (n_cmdline_args) 352 exe_name = rtl::OUString(cmdline_args[0]); 353 354 rtl::OUString exe_url = exe_name; 355 if (search_path) 356 osl_searchFileURL(exe_name.pData, NULL, &exe_url.pData); 357 358 rtl::OUString exe_path; 359 if (osl_File_E_None != osl::FileBase::getSystemPathFromFileURL(exe_url, exe_path)) 360 return rtl::OUString(); 361 362 exe_path = getShortPath(exe_path, rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(".exe"))); 363 364 if (exe_path.indexOf(' ') != -1) 365 exe_path = quote_string(exe_path); 366 367 return exe_path; 368 } 369 370 //########################################################## 371 rtl::OUString get_file_extension(const rtl::OUString& file_name) 372 { 373 sal_Int32 index = file_name.lastIndexOf('.'); 374 if ((index != -1) && ((index + 1) < file_name.getLength())) 375 return file_name.copy(index + 1); 376 377 return rtl::OUString(); 378 } 379 380 //########################################################## 381 bool is_batch_file(const rtl::OUString& file_name) 382 { 383 rtl::OUString ext = get_file_extension(file_name); 384 return (ext.equalsIgnoreAsciiCaseAscii("bat") || 385 ext.equalsIgnoreAsciiCaseAscii("cmd") || 386 ext.equalsIgnoreAsciiCaseAscii("btm")); 387 } 388 389 //########################################################## 390 rtl::OUString get_batch_processor() 391 { 392 rtl::OUString comspec; 393 osl_getEnvironment(ENV_COMSPEC.pData, &comspec.pData); 394 395 OSL_ASSERT(comspec.getLength()); 396 397 /* check if comspec path contains blanks and quote it if any */ 398 if (comspec.indexOf(' ') != -1) 399 comspec = quote_string(comspec); 400 401 return comspec; 402 } 403 404 } // namespace private 405 406 407 //################################################# 408 oslProcessError SAL_CALL osl_executeProcess( 409 rtl_uString *strImageName, 410 rtl_uString *strArguments[], 411 sal_uInt32 nArguments, 412 oslProcessOption Options, 413 oslSecurity Security, 414 rtl_uString *strDirectory, 415 rtl_uString *strEnvironmentVars[], 416 sal_uInt32 nEnvironmentVars, 417 oslProcess *pProcess 418 ) 419 { 420 return osl_executeProcess_WithRedirectedIO( 421 strImageName, 422 strArguments, 423 nArguments, 424 Options, 425 Security, 426 strDirectory, 427 strEnvironmentVars, 428 nEnvironmentVars, 429 pProcess, 430 NULL, NULL, NULL ); 431 } 432 433 //################################################# 434 oslProcessError SAL_CALL osl_executeProcess_WithRedirectedIO( 435 rtl_uString *ustrImageName, 436 rtl_uString *ustrArguments[], 437 sal_uInt32 nArguments, 438 oslProcessOption Options, 439 oslSecurity Security, 440 rtl_uString *ustrDirectory, 441 rtl_uString *ustrEnvironmentVars[], 442 sal_uInt32 nEnvironmentVars, 443 oslProcess *pProcess, 444 oslFileHandle *pProcessInputWrite, 445 oslFileHandle *pProcessOutputRead, 446 oslFileHandle *pProcessErrorRead) 447 { 448 rtl::OUString exe_path = get_executable_path( 449 ustrImageName, ustrArguments, nArguments, (Options & osl_Process_SEARCHPATH)); 450 451 if (0 == exe_path.getLength()) 452 return osl_Process_E_NotFound; 453 454 if (pProcess == NULL) 455 return osl_Process_E_InvalidError; 456 457 DWORD flags = NORMAL_PRIORITY_CLASS; 458 rtl::OUStringBuffer command_line; 459 460 if (is_batch_file(exe_path)) 461 { 462 rtl::OUString batch_processor = get_batch_processor(); 463 464 if (batch_processor.getLength()) 465 { 466 /* cmd.exe does not work without a console window */ 467 if (!(Options & osl_Process_WAIT) || (Options & osl_Process_DETACHED)) 468 flags |= CREATE_NEW_CONSOLE; 469 470 command_line.append(batch_processor); 471 command_line.appendAscii(" /c "); 472 } 473 else 474 // should we return here in case of error? 475 return osl_Process_E_Unknown; 476 } 477 478 command_line.append(exe_path); 479 480 /* Add remaining arguments to command line. If ustrImageName is NULL 481 the first parameter is the name of the executable so we have to 482 start at 1 instead of 0 */ 483 for (sal_uInt32 n = (NULL != ustrImageName) ? 0 : 1; n < nArguments; n++) 484 { 485 command_line.appendAscii(SPACE); 486 487 /* Quote arguments containing blanks */ 488 if (rtl::OUString(ustrArguments[n]).indexOf(' ') != -1) 489 command_line.append(quote_string(ustrArguments[n])); 490 else 491 command_line.append(ustrArguments[n]); 492 } 493 494 environment_container_t environment; 495 LPVOID p_environment = NULL; 496 497 if (nEnvironmentVars && ustrEnvironmentVars) 498 { 499 if (!setup_process_environment( 500 ustrEnvironmentVars, nEnvironmentVars, environment)) 501 return osl_Process_E_InvalidError; 502 503 flags |= CREATE_UNICODE_ENVIRONMENT; 504 p_environment = &environment[0]; 505 } 506 507 rtl::OUString cwd; 508 if (ustrDirectory && ustrDirectory->length && (osl_File_E_None != osl::FileBase::getSystemPathFromFileURL(ustrDirectory, cwd))) 509 return osl_Process_E_InvalidError; 510 511 LPCWSTR p_cwd = (cwd.getLength()) ? reinterpret_cast<LPCWSTR>(cwd.getStr()) : NULL; 512 513 if ((Options & osl_Process_DETACHED) && !(flags & CREATE_NEW_CONSOLE)) 514 flags |= DETACHED_PROCESS; 515 516 STARTUPINFO startup_info; 517 memset(&startup_info, 0, sizeof(STARTUPINFO)); 518 519 startup_info.cb = sizeof(STARTUPINFO); 520 startup_info.dwFlags = STARTF_USESHOWWINDOW; 521 startup_info.lpDesktop = L""; 522 523 /* Create pipes for redirected IO */ 524 HANDLE hInputRead = NULL; 525 HANDLE hInputWrite = NULL; 526 if (pProcessInputWrite && create_pipe(&hInputRead, true, &hInputWrite, false)) 527 startup_info.hStdInput = hInputRead; 528 529 HANDLE hOutputRead = NULL; 530 HANDLE hOutputWrite = NULL; 531 if (pProcessOutputRead && create_pipe(&hOutputRead, false, &hOutputWrite, true)) 532 startup_info.hStdOutput = hOutputWrite; 533 534 HANDLE hErrorRead = NULL; 535 HANDLE hErrorWrite = NULL; 536 if (pProcessErrorRead && create_pipe(&hErrorRead, false, &hErrorWrite, true)) 537 startup_info.hStdError = hErrorWrite; 538 539 bool b_inherit_handles = false; 540 if (pProcessInputWrite || pProcessOutputRead || pProcessErrorRead) 541 { 542 startup_info.dwFlags |= STARTF_USESTDHANDLES; 543 b_inherit_handles = true; 544 } 545 546 switch(Options & (osl_Process_NORMAL | osl_Process_HIDDEN | osl_Process_MINIMIZED | osl_Process_MAXIMIZED | osl_Process_FULLSCREEN)) 547 { 548 case osl_Process_HIDDEN: 549 startup_info.wShowWindow = SW_HIDE; 550 flags |= CREATE_NO_WINDOW; // ignored for non-console 551 // applications; ignored on 552 // Win9x 553 break; 554 555 case osl_Process_MINIMIZED: 556 startup_info.wShowWindow = SW_MINIMIZE; 557 break; 558 559 case osl_Process_MAXIMIZED: 560 case osl_Process_FULLSCREEN: 561 startup_info.wShowWindow = SW_MAXIMIZE; 562 break; 563 564 default: 565 startup_info.wShowWindow = SW_NORMAL; 566 } 567 568 rtl::OUString cmdline = command_line.makeStringAndClear(); 569 PROCESS_INFORMATION process_info; 570 BOOL bRet = FALSE; 571 572 if ((Security != NULL) && (((oslSecurityImpl*)Security)->m_hToken != NULL)) 573 { 574 bRet = CreateProcessAsUser( 575 ((oslSecurityImpl*)Security)->m_hToken, 576 NULL, const_cast<LPTSTR>(reinterpret_cast<LPCTSTR>(cmdline.getStr())), NULL, NULL, 577 b_inherit_handles, flags, p_environment, p_cwd, 578 &startup_info, &process_info); 579 } 580 else 581 { 582 bRet = CreateProcess( 583 NULL, const_cast<LPTSTR>(reinterpret_cast<LPCTSTR>(cmdline.getStr())), NULL, NULL, 584 b_inherit_handles, flags, p_environment, p_cwd, 585 &startup_info, &process_info); 586 } 587 588 /* Now we can close the pipe ends that are used by the child process */ 589 590 if (hInputRead) 591 CloseHandle(hInputRead); 592 593 if (hOutputWrite) 594 CloseHandle(hOutputWrite); 595 596 if (hErrorWrite) 597 CloseHandle(hErrorWrite); 598 599 if (bRet) 600 { 601 CloseHandle(process_info.hThread); 602 603 oslProcessImpl* pProcImpl = reinterpret_cast<oslProcessImpl*>( 604 rtl_allocateMemory(sizeof(oslProcessImpl))); 605 606 if (pProcImpl != NULL) 607 { 608 pProcImpl->m_hProcess = process_info.hProcess; 609 pProcImpl->m_IdProcess = process_info.dwProcessId; 610 611 *pProcess = (oslProcess)pProcImpl; 612 613 if (Options & osl_Process_WAIT) 614 WaitForSingleObject(pProcImpl->m_hProcess, INFINITE); 615 616 if (pProcessInputWrite) 617 *pProcessInputWrite = osl_createFileHandleFromOSHandle(hInputWrite, osl_File_OpenFlag_Write); 618 619 if (pProcessOutputRead) 620 *pProcessOutputRead = osl_createFileHandleFromOSHandle(hOutputRead, osl_File_OpenFlag_Read); 621 622 if (pProcessErrorRead) 623 *pProcessErrorRead = osl_createFileHandleFromOSHandle(hErrorRead, osl_File_OpenFlag_Read); 624 625 return osl_Process_E_None; 626 } 627 } 628 629 /* if an error occured we have to close the server side pipe ends too */ 630 631 if (hInputWrite) 632 CloseHandle(hInputWrite); 633 634 if (hOutputRead) 635 CloseHandle(hOutputRead); 636 637 if (hErrorRead) 638 CloseHandle(hErrorRead); 639 640 return osl_Process_E_Unknown; 641 } 642