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