1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*************************************************************************
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 * Copyright 2000, 2010 Oracle and/or its affiliates.
8 * OpenOffice.org - a multi-platform office productivity suite
10 * This file is part of OpenOffice.org.
12 * OpenOffice.org is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU Lesser General Public License version 3
14 * only, as published by the Free Software Foundation.
16 * OpenOffice.org is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License version 3 for more details
20 * (a copy is included in the LICENSE file that accompanied this code).
22 * You should have received a copy of the GNU Lesser General Public License
23 * version 3 along with OpenOffice.org. If not, see
24 * <http://www.openoffice.org/license.html>
25 * for a copy of the LGPLv3 License.
27 ************************************************************************/
33 #ifndef WIN32_LEAN_AND_MEAN
34 # define WIN32_LEAN_AND_MEAN
36 # pragma warning(push,1) /* disable warnings within system headers */
43 # undef WIN32_LEAN_AND_MEAN
46 #include <rtl/ustring.hxx>
47 #include <rtl/ustrbuf.hxx>
49 #include "rtl/allocator.hxx"
50 #include <osl/file.hxx>
57 //#################################################
58 extern "C" oslFileHandle SAL_CALL
osl_createFileHandleFromOSHandle( HANDLE hFile
, sal_uInt32 uFlags
);
60 //#################################################
61 const sal_Unicode NAME_VALUE_SEPARATOR
= TEXT('=');
62 const sal_Char
* SPACE
= " ";
63 const rtl::OUString
ENV_COMSPEC ("COMSPEC");
64 const rtl::OUString
QUOTE("\"");
66 namespace /* private */
68 //#################################################
69 typedef std::list
<rtl::OUString
> string_container_t
;
70 typedef string_container_t::iterator string_container_iterator_t
;
71 typedef string_container_t::const_iterator string_container_const_iterator_t
;
72 typedef std::pair
<string_container_iterator_t
, string_container_iterator_t
> iterator_pair_t
;
73 typedef std::vector
<sal_Unicode
> environment_container_t
;
75 //#################################################
76 /* Function object that compares two strings that are
77 expected to be environment variables in the form
78 "name=value". Only the 'name' part will be compared.
79 The comparison is in upper case and returns true
80 if the first of both strings is less than the
82 struct less_environment_variable
:
83 public std::binary_function
<rtl::OUString
, rtl::OUString
, bool>
85 bool operator() (const rtl::OUString
& lhs
, const rtl::OUString
& rhs
) const
87 OSL_ENSURE((lhs
.indexOf(NAME_VALUE_SEPARATOR
) > -1) && \
88 (rhs
.indexOf(NAME_VALUE_SEPARATOR
) > -1), \
89 "Malformed environment variable");
91 // Windows compares environment variables uppercase
93 return (rtl_ustr_compare_WithLength(
94 lhs
.toAsciiUpperCase().pData
->buffer
,
95 lhs
.indexOf(NAME_VALUE_SEPARATOR
),
96 rhs
.toAsciiUpperCase().pData
->buffer
,
97 rhs
.indexOf(NAME_VALUE_SEPARATOR
)) < 0);
101 //#################################################
102 /* Function object used by for_each algorithm to
103 calculate the sum of the length of all strings
104 in a string container. */
105 class sum_of_string_lengths
108 //--------------------------------
109 sum_of_string_lengths() : sum_(0) {}
111 //--------------------------------
112 void operator() (const rtl::OUString
& string
)
114 OSL_ASSERT(string
.getLength());
116 // always include the terminating '\0'
117 if (string
.getLength())
118 sum_
+= string
.getLength() + 1;
121 //--------------------------------
122 operator size_t () const
130 //#################################################
131 inline size_t calc_sum_of_string_lengths(const string_container_t
& string_cont
)
133 return std::for_each(
134 string_cont
.begin(), string_cont
.end(), sum_of_string_lengths());
137 //#################################################
138 void read_environment(/*out*/ string_container_t
* environment
)
140 // GetEnvironmentStrings returns a sorted list, Windows
141 // sorts environment variables upper case
142 LPTSTR env
= reinterpret_cast<LPTSTR
>(GetEnvironmentStrings());
145 while (size_t l
= _tcslen(p
))
147 environment
->push_back(reinterpret_cast<const sal_Unicode
*>(p
));
150 FreeEnvironmentStrings(env
);
153 //#################################################
154 /* the environment list must be sorted, new values
155 should either replace existing ones or should be
156 added to the list, environment variables will
157 be handled case-insensitive */
158 bool create_merged_environment(
159 rtl_uString
* env_vars
[],
160 sal_uInt32 env_vars_count
,
161 /*in|out*/ string_container_t
* merged_env
)
163 OSL_ASSERT(env_vars
&& env_vars_count
> 0 && merged_env
);
165 read_environment(merged_env
);
167 for (sal_uInt32 i
= 0; i
< env_vars_count
; i
++)
169 rtl::OUString env_var
= rtl::OUString(env_vars
[i
]);
171 if (env_var
.getLength() == 0)
174 iterator_pair_t iter_pair
= std::equal_range(
178 less_environment_variable());
180 if (env_var
.indexOf(NAME_VALUE_SEPARATOR
) == -1)
182 merged_env
->erase(iter_pair
.first
, iter_pair
.second
);
186 if (iter_pair
.first
!= iter_pair
.second
) // found
187 *iter_pair
.first
= env_var
;
189 merged_env
->insert(iter_pair
.first
, env_var
);
195 //#################################################
196 /* Create a merged environment */
197 bool setup_process_environment(
198 rtl_uString
* environment_vars
[],
199 sal_uInt32 n_environment_vars
,
200 /*in|out*/ environment_container_t
& environment
)
202 string_container_t merged_env
;
203 if (!create_merged_environment(environment_vars
, n_environment_vars
, &merged_env
))
206 // allocate enough space for the '\0'-separated environment strings and
208 environment
.resize(calc_sum_of_string_lengths(merged_env
) + 1);
210 string_container_const_iterator_t iter
= merged_env
.begin();
211 string_container_const_iterator_t iter_end
= merged_env
.end();
214 for (/**/; iter
!= iter_end
; ++iter
)
216 rtl::OUString envv
= *iter
;
218 OSL_ASSERT(envv
.getLength());
220 sal_uInt32 n
= envv
.getLength() + 1; // copy the final '\0', too
222 reinterpret_cast<void*>(&environment
[pos
]),
223 reinterpret_cast<const void*>(envv
.getStr()),
224 n
* sizeof(sal_Unicode
));
227 environment
[pos
] = 0; // append a final '\0'
232 //##########################################################
233 /* In contrast to the Win32 API function CreatePipe with
234 this function the caller is able to determine separately
235 which handle of the pipe is inheritable. */
238 bool b_read_pipe_inheritable
,
239 PHANDLE p_write_pipe
,
240 bool b_write_pipe_inheritable
,
241 LPVOID p_security_descriptor
= NULL
,
244 SECURITY_ATTRIBUTES sa
;
245 sa
.nLength
= sizeof(SECURITY_ATTRIBUTES
);
246 sa
.lpSecurityDescriptor
= p_security_descriptor
;
247 sa
.bInheritHandle
= b_read_pipe_inheritable
|| b_write_pipe_inheritable
;
252 if (!b_read_pipe_inheritable
&& b_write_pipe_inheritable
)
254 bRet
= CreatePipe(&hTemp
, p_write_pipe
, &sa
, pipe_size
);
256 if (bRet
&& !DuplicateHandle(GetCurrentProcess(), hTemp
,
257 GetCurrentProcess(), p_read_pipe
, 0, FALSE
,
258 DUPLICATE_CLOSE_SOURCE
| DUPLICATE_SAME_ACCESS
))
261 CloseHandle(*p_read_pipe
);
265 else if (b_read_pipe_inheritable
&& !b_write_pipe_inheritable
)
267 bRet
= CreatePipe(p_read_pipe
, &hTemp
, &sa
, pipe_size
);
269 if (bRet
&& !DuplicateHandle(GetCurrentProcess(), hTemp
,
270 GetCurrentProcess(), p_write_pipe
, 0, FALSE
,
271 DUPLICATE_CLOSE_SOURCE
| DUPLICATE_SAME_ACCESS
))
274 CloseHandle(*p_write_pipe
);
280 bRet
= CreatePipe(p_read_pipe
, p_write_pipe
, &sa
, pipe_size
);
285 //#########################################################
286 // Add a quote sign to the start and the end of a string
287 // if not already present
288 rtl::OUString
quote_string(const rtl::OUString
& string
)
290 rtl::OUStringBuffer quoted
;
291 if (string
.indexOf(QUOTE
) != 0)
292 quoted
.append(QUOTE
);
294 quoted
.append(string
);
296 if (string
.lastIndexOf(QUOTE
) != (string
.getLength() - 1))
297 quoted
.append(QUOTE
);
299 return quoted
.makeStringAndClear();
302 //The parameter path must be a system path. If it is longer than 260 characters
303 //then it is shortened using the GetShortPathName function. This function only
304 //works if the path exists. Because "path" can be the path to an executable, it
305 //may not have the file extension ".exe". However, if the file on disk has the
306 //".exe" extension, then the function will fail. In this case a second attempt
307 //is started by adding the parameter "extension" to "path".
308 rtl::OUString
getShortPath(rtl::OUString
const & path
, rtl::OUString
const & extension
)
310 rtl::OUString
ret(path
);
311 if (path
.getLength() > 260)
313 std::vector
<sal_Unicode
> vec(path
.getLength() + 1);
314 //GetShortPathNameW only works if the file can be found!
315 const DWORD len
= GetShortPathNameW(
316 reinterpret_cast<LPCWSTR
>(path
.getStr()), reinterpret_cast<LPWSTR
>(&vec
[0]), path
.getLength() + 1);
318 if (!len
&& GetLastError() == ERROR_FILE_NOT_FOUND
319 && extension
.getLength())
321 const rtl::OUString
extPath(path
+ extension
);
322 std::vector
<sal_Unicode
> vec2(
323 extPath
.getLength() + 1);
324 const DWORD len2
= GetShortPathNameW(
325 reinterpret_cast<LPCWSTR
>(extPath
.getStr()), reinterpret_cast<LPWSTR
>(&vec2
[0]), extPath
.getLength() + 1);
326 ret
= rtl::OUString(&vec2
[0], len2
);
330 ret
= rtl::OUString(&vec
[0], len
);
335 //##########################################################
336 // Returns the system path of the executable which can either
337 // be provided via the strImageName parameter or as first
338 // element of the strArguments list.
339 // The returned path will be quoted if it contains spaces.
340 rtl::OUString
get_executable_path(
341 rtl_uString
* image_name
,
342 rtl_uString
* cmdline_args
[],
343 sal_uInt32 n_cmdline_args
,
346 rtl::OUString exe_name
;
349 exe_name
= image_name
;
350 else if (n_cmdline_args
)
351 exe_name
= rtl::OUString(cmdline_args
[0]);
353 rtl::OUString exe_url
= exe_name
;
355 osl_searchFileURL(exe_name
.pData
, NULL
, &exe_url
.pData
);
357 rtl::OUString exe_path
;
358 if (osl::FileBase::E_None
!= osl::FileBase::getSystemPathFromFileURL(exe_url
, exe_path
))
359 return rtl::OUString();
361 exe_path
= getShortPath(exe_path
, rtl::OUString(".exe"));
363 if (exe_path
.indexOf(' ') != -1)
364 exe_path
= quote_string(exe_path
);
369 //##########################################################
370 rtl::OUString
get_file_extension(const rtl::OUString
& file_name
)
372 sal_Int32 index
= file_name
.lastIndexOf('.');
373 if ((index
!= -1) && ((index
+ 1) < file_name
.getLength()))
374 return file_name
.copy(index
+ 1);
376 return rtl::OUString();
379 //##########################################################
380 bool is_batch_file(const rtl::OUString
& file_name
)
382 rtl::OUString ext
= get_file_extension(file_name
);
383 return (ext
.equalsIgnoreAsciiCaseAsciiL(RTL_CONSTASCII_STRINGPARAM("bat")) ||
384 ext
.equalsIgnoreAsciiCaseAsciiL(RTL_CONSTASCII_STRINGPARAM("cmd")) ||
385 ext
.equalsIgnoreAsciiCaseAsciiL(RTL_CONSTASCII_STRINGPARAM("btm")));
388 //##########################################################
389 rtl::OUString
get_batch_processor()
391 rtl::OUString comspec
;
392 osl_getEnvironment(ENV_COMSPEC
.pData
, &comspec
.pData
);
394 OSL_ASSERT(comspec
.getLength());
396 /* check if comspec path contains blanks and quote it if any */
397 if (comspec
.indexOf(' ') != -1)
398 comspec
= quote_string(comspec
);
403 } // namespace private
406 //#################################################
407 oslProcessError SAL_CALL
osl_executeProcess(
408 rtl_uString
*strImageName
,
409 rtl_uString
*strArguments
[],
410 sal_uInt32 nArguments
,
411 oslProcessOption Options
,
412 oslSecurity Security
,
413 rtl_uString
*strDirectory
,
414 rtl_uString
*strEnvironmentVars
[],
415 sal_uInt32 nEnvironmentVars
,
419 return osl_executeProcess_WithRedirectedIO(
432 //#################################################
433 oslProcessError SAL_CALL
osl_executeProcess_WithRedirectedIO(
434 rtl_uString
*ustrImageName
,
435 rtl_uString
*ustrArguments
[],
436 sal_uInt32 nArguments
,
437 oslProcessOption Options
,
438 oslSecurity Security
,
439 rtl_uString
*ustrDirectory
,
440 rtl_uString
*ustrEnvironmentVars
[],
441 sal_uInt32 nEnvironmentVars
,
442 oslProcess
*pProcess
,
443 oslFileHandle
*pProcessInputWrite
,
444 oslFileHandle
*pProcessOutputRead
,
445 oslFileHandle
*pProcessErrorRead
)
447 rtl::OUString exe_path
= get_executable_path(
448 ustrImageName
, ustrArguments
, nArguments
, (Options
& osl_Process_SEARCHPATH
));
450 if (0 == exe_path
.getLength())
451 return osl_Process_E_NotFound
;
453 if (pProcess
== NULL
)
454 return osl_Process_E_InvalidError
;
456 DWORD flags
= NORMAL_PRIORITY_CLASS
;
457 rtl::OUStringBuffer command_line
;
459 if (is_batch_file(exe_path
))
461 rtl::OUString batch_processor
= get_batch_processor();
463 if (batch_processor
.getLength())
465 /* cmd.exe does not work without a console window */
466 if (!(Options
& osl_Process_WAIT
) || (Options
& osl_Process_DETACHED
))
467 flags
|= CREATE_NEW_CONSOLE
;
469 command_line
.append(batch_processor
);
470 command_line
.appendAscii(" /c ");
473 // should we return here in case of error?
474 return osl_Process_E_Unknown
;
477 command_line
.append(exe_path
);
479 /* Add remaining arguments to command line. If ustrImageName is NULL
480 the first parameter is the name of the executable so we have to
481 start at 1 instead of 0 */
482 for (sal_uInt32 n
= (NULL
!= ustrImageName
) ? 0 : 1; n
< nArguments
; n
++)
484 command_line
.appendAscii(SPACE
);
486 /* Quote arguments containing blanks */
487 if (rtl::OUString(ustrArguments
[n
]).indexOf(' ') != -1)
488 command_line
.append(quote_string(ustrArguments
[n
]));
490 command_line
.append(ustrArguments
[n
]);
493 environment_container_t environment
;
494 LPVOID p_environment
= NULL
;
496 if (nEnvironmentVars
&& ustrEnvironmentVars
)
498 if (!setup_process_environment(
499 ustrEnvironmentVars
, nEnvironmentVars
, environment
))
500 return osl_Process_E_InvalidError
;
502 flags
|= CREATE_UNICODE_ENVIRONMENT
;
503 p_environment
= &environment
[0];
507 if (ustrDirectory
&& ustrDirectory
->length
&& (osl::FileBase::E_None
!= osl::FileBase::getSystemPathFromFileURL(ustrDirectory
, cwd
)))
508 return osl_Process_E_InvalidError
;
510 LPCWSTR p_cwd
= (cwd
.getLength()) ? reinterpret_cast<LPCWSTR
>(cwd
.getStr()) : NULL
;
512 if ((Options
& osl_Process_DETACHED
) && !(flags
& CREATE_NEW_CONSOLE
))
513 flags
|= DETACHED_PROCESS
;
515 STARTUPINFO startup_info
;
516 memset(&startup_info
, 0, sizeof(STARTUPINFO
));
518 startup_info
.cb
= sizeof(STARTUPINFO
);
519 startup_info
.dwFlags
= STARTF_USESHOWWINDOW
;
520 startup_info
.lpDesktop
= const_cast<LPWSTR
>(L
"");
522 /* Create pipes for redirected IO */
523 HANDLE hInputRead
= NULL
;
524 HANDLE hInputWrite
= NULL
;
525 if (pProcessInputWrite
&& create_pipe(&hInputRead
, true, &hInputWrite
, false))
526 startup_info
.hStdInput
= hInputRead
;
528 HANDLE hOutputRead
= NULL
;
529 HANDLE hOutputWrite
= NULL
;
530 if (pProcessOutputRead
&& create_pipe(&hOutputRead
, false, &hOutputWrite
, true))
531 startup_info
.hStdOutput
= hOutputWrite
;
533 HANDLE hErrorRead
= NULL
;
534 HANDLE hErrorWrite
= NULL
;
535 if (pProcessErrorRead
&& create_pipe(&hErrorRead
, false, &hErrorWrite
, true))
536 startup_info
.hStdError
= hErrorWrite
;
538 bool b_inherit_handles
= false;
539 if (pProcessInputWrite
|| pProcessOutputRead
|| pProcessErrorRead
)
541 startup_info
.dwFlags
|= STARTF_USESTDHANDLES
;
542 b_inherit_handles
= true;
545 switch(Options
& (osl_Process_NORMAL
| osl_Process_HIDDEN
| osl_Process_MINIMIZED
| osl_Process_MAXIMIZED
| osl_Process_FULLSCREEN
))
547 case osl_Process_HIDDEN
:
548 startup_info
.wShowWindow
= SW_HIDE
;
549 flags
|= CREATE_NO_WINDOW
; // ignored for non-console
550 // applications; ignored on
554 case osl_Process_MINIMIZED
:
555 startup_info
.wShowWindow
= SW_MINIMIZE
;
558 case osl_Process_MAXIMIZED
:
559 case osl_Process_FULLSCREEN
:
560 startup_info
.wShowWindow
= SW_MAXIMIZE
;
564 startup_info
.wShowWindow
= SW_NORMAL
;
567 rtl::OUString cmdline
= command_line
.makeStringAndClear();
568 PROCESS_INFORMATION process_info
;
571 if ((Security
!= NULL
) && (((oslSecurityImpl
*)Security
)->m_hToken
!= NULL
))
573 bRet
= CreateProcessAsUser(
574 ((oslSecurityImpl
*)Security
)->m_hToken
,
575 NULL
, const_cast<LPTSTR
>(reinterpret_cast<LPCTSTR
>(cmdline
.getStr())), NULL
, NULL
,
576 b_inherit_handles
, flags
, p_environment
, p_cwd
,
577 &startup_info
, &process_info
);
581 bRet
= CreateProcess(
582 NULL
, const_cast<LPTSTR
>(reinterpret_cast<LPCTSTR
>(cmdline
.getStr())), NULL
, NULL
,
583 b_inherit_handles
, flags
, p_environment
, p_cwd
,
584 &startup_info
, &process_info
);
587 /* Now we can close the pipe ends that are used by the child process */
590 CloseHandle(hInputRead
);
593 CloseHandle(hOutputWrite
);
596 CloseHandle(hErrorWrite
);
600 CloseHandle(process_info
.hThread
);
602 oslProcessImpl
* pProcImpl
= reinterpret_cast<oslProcessImpl
*>(
603 rtl_allocateMemory(sizeof(oslProcessImpl
)));
605 if (pProcImpl
!= NULL
)
607 pProcImpl
->m_hProcess
= process_info
.hProcess
;
608 pProcImpl
->m_IdProcess
= process_info
.dwProcessId
;
610 *pProcess
= (oslProcess
)pProcImpl
;
612 if (Options
& osl_Process_WAIT
)
613 WaitForSingleObject(pProcImpl
->m_hProcess
, INFINITE
);
615 if (pProcessInputWrite
)
616 *pProcessInputWrite
= osl_createFileHandleFromOSHandle(hInputWrite
, osl_File_OpenFlag_Write
);
618 if (pProcessOutputRead
)
619 *pProcessOutputRead
= osl_createFileHandleFromOSHandle(hOutputRead
, osl_File_OpenFlag_Read
);
621 if (pProcessErrorRead
)
622 *pProcessErrorRead
= osl_createFileHandleFromOSHandle(hErrorRead
, osl_File_OpenFlag_Read
);
624 return osl_Process_E_None
;
628 /* if an error occurred we have to close the server side pipe ends too */
631 CloseHandle(hInputWrite
);
634 CloseHandle(hOutputRead
);
637 CloseHandle(hErrorRead
);
639 return osl_Process_E_Unknown
;
642 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */