1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: procimpl.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sal.hxx"
37 #ifndef WIN32_LEAN_AND_MEAN
38 # define WIN32_LEAN_AND_MEAN
40 # pragma warning(push,1) /* disable warnings within system headers */
47 # undef WIN32_LEAN_AND_MEAN
50 #include <rtl/ustring.hxx>
51 #include <rtl/ustrbuf.hxx>
53 #include "rtl/allocator.hxx"
54 #include <osl/file.hxx>
61 //#################################################
62 extern "C" oslFileHandle SAL_CALL
osl_createFileHandleFromOSHandle( HANDLE hFile
, sal_uInt32 uFlags
);
64 //#################################################
65 const sal_Unicode NAME_VALUE_SEPARATOR
= TEXT('=');
66 const sal_Char
* SPACE
= " ";
67 const rtl::OUString ENV_COMSPEC
= rtl::OUString::createFromAscii("COMSPEC");
68 const rtl::OUString QUOTE
= rtl::OUString::createFromAscii("\"");
70 namespace /* private */
72 //#################################################
73 typedef std::list
<rtl::OUString
, rtl::Allocator
<rtl::OUString
> > string_container_t
;
74 typedef string_container_t::iterator string_container_iterator_t
;
75 typedef string_container_t::const_iterator string_container_const_iterator_t
;
76 typedef std::pair
<string_container_iterator_t
, string_container_iterator_t
> iterator_pair_t
;
77 typedef std::vector
<sal_Unicode
, rtl::Allocator
<sal_Unicode
> > environment_container_t
;
79 //#################################################
80 /* Function object that compares two strings that are
81 expected to be environment variables in the form
82 "name=value". Only the 'name' part will be compared.
83 The comparison is in upper case and returns true
84 if the first of both strings is less than the
86 struct less_environment_variable
:
87 public std::binary_function
<rtl::OUString
, rtl::OUString
, bool>
89 bool operator() (const rtl::OUString
& lhs
, const rtl::OUString
& rhs
) const
91 OSL_ENSURE((lhs
.indexOf(NAME_VALUE_SEPARATOR
) > -1) && \
92 (rhs
.indexOf(NAME_VALUE_SEPARATOR
) > -1), \
93 "Malformed environment variable");
95 // Windows compares environment variables uppercase
97 return (rtl_ustr_compare_WithLength(
98 lhs
.toAsciiUpperCase().pData
->buffer
,
99 lhs
.indexOf(NAME_VALUE_SEPARATOR
),
100 rhs
.toAsciiUpperCase().pData
->buffer
,
101 rhs
.indexOf(NAME_VALUE_SEPARATOR
)) < 0);
105 //#################################################
106 /* Function object used by for_each algorithm to
107 calculate the sum of the length of all strings
108 in a string container. */
109 class sum_of_string_lengths
112 //--------------------------------
113 sum_of_string_lengths() : sum_(0) {}
115 //--------------------------------
116 void operator() (const rtl::OUString
& string
)
118 OSL_ASSERT(string
.getLength());
120 // always include the terminating '\0'
121 if (string
.getLength())
122 sum_
+= string
.getLength() + 1;
125 //--------------------------------
126 operator size_t () const
134 //#################################################
135 inline size_t calc_sum_of_string_lengths(const string_container_t
& string_cont
)
137 return std::for_each(
138 string_cont
.begin(), string_cont
.end(), sum_of_string_lengths());
141 //#################################################
142 void read_environment(/*out*/ string_container_t
* environment
)
144 // GetEnvironmentStrings returns a sorted list, Windows
145 // sorts environment variables upper case
146 LPTSTR env
= reinterpret_cast<LPTSTR
>(GetEnvironmentStrings());
149 while (size_t l
= _tcslen(p
))
151 environment
->push_back(reinterpret_cast<const sal_Unicode
*>(p
));
154 FreeEnvironmentStrings(env
);
157 //#################################################
158 /* the environment list must be sorted, new values
159 should either replace existing ones or should be
160 added to the list, environment variables will
161 be handled case-insensitive */
162 bool create_merged_environment(
163 rtl_uString
* env_vars
[],
164 sal_uInt32 env_vars_count
,
165 /*in|out*/ string_container_t
* merged_env
)
167 OSL_ASSERT(env_vars
&& env_vars_count
> 0 && merged_env
);
169 read_environment(merged_env
);
171 for (sal_uInt32 i
= 0; i
< env_vars_count
; i
++)
173 rtl::OUString env_var
= rtl::OUString(env_vars
[i
]);
175 if ((env_var
.getLength() == 0) ||
176 (env_var
.indexOf(NAME_VALUE_SEPARATOR
) == -1))
179 iterator_pair_t iter_pair
= std::equal_range(
183 less_environment_variable());
185 if (iter_pair
.first
!= iter_pair
.second
) // found
186 *iter_pair
.first
= env_var
;
188 merged_env
->insert(iter_pair
.first
, env_var
);
193 //#################################################
194 /* Create a merged environment */
195 bool setup_process_environment(
196 rtl_uString
* environment_vars
[],
197 sal_uInt32 n_environment_vars
,
198 /*in|out*/ environment_container_t
& environment
)
200 string_container_t merged_env
;
201 if (!create_merged_environment(environment_vars
, n_environment_vars
, &merged_env
))
204 // reserve enough space for the '\0'-separated environment strings and
206 environment
.reserve(calc_sum_of_string_lengths(merged_env
) + 1);
208 string_container_const_iterator_t iter
= merged_env
.begin();
209 string_container_const_iterator_t iter_end
= merged_env
.end();
212 for (/**/; iter
!= iter_end
; ++iter
)
214 rtl::OUString envv
= *iter
;
216 OSL_ASSERT(envv
.getLength());
218 sal_uInt32 n
= envv
.getLength() + 1; // copy the final '\0', too
220 reinterpret_cast<void*>(&environment
[pos
]),
221 reinterpret_cast<const void*>(envv
.getStr()),
222 n
* sizeof(sal_Unicode
));
225 environment
[pos
] = 0; // append a final '\0'
230 //##########################################################
231 /* In contrast to the Win32 API function CreatePipe with
232 this function the caller is able to determine separately
233 which handle of the pipe is inheritable. */
236 bool b_read_pipe_inheritable
,
237 PHANDLE p_write_pipe
,
238 bool b_write_pipe_inheritable
,
239 LPVOID p_security_descriptor
= NULL
,
242 SECURITY_ATTRIBUTES sa
;
243 sa
.nLength
= sizeof(SECURITY_ATTRIBUTES
);
244 sa
.lpSecurityDescriptor
= p_security_descriptor
;
245 sa
.bInheritHandle
= b_read_pipe_inheritable
|| b_write_pipe_inheritable
;
250 if (!b_read_pipe_inheritable
&& b_write_pipe_inheritable
)
252 bRet
= CreatePipe(&hTemp
, p_write_pipe
, &sa
, pipe_size
);
254 if (bRet
&& !DuplicateHandle(GetCurrentProcess(), hTemp
,
255 GetCurrentProcess(), p_read_pipe
, 0, FALSE
,
256 DUPLICATE_CLOSE_SOURCE
| DUPLICATE_SAME_ACCESS
))
259 CloseHandle(*p_read_pipe
);
263 else if (b_read_pipe_inheritable
&& !b_write_pipe_inheritable
)
265 bRet
= CreatePipe(p_read_pipe
, &hTemp
, &sa
, pipe_size
);
267 if (bRet
&& !DuplicateHandle(GetCurrentProcess(), hTemp
,
268 GetCurrentProcess(), p_write_pipe
, 0, FALSE
,
269 DUPLICATE_CLOSE_SOURCE
| DUPLICATE_SAME_ACCESS
))
272 CloseHandle(*p_write_pipe
);
278 bRet
= CreatePipe(p_read_pipe
, p_write_pipe
, &sa
, pipe_size
);
283 //#########################################################
284 // Add a quote sign to the start and the end of a string
285 // if not already present
286 rtl::OUString
quote_string(const rtl::OUString
& string
)
288 rtl::OUStringBuffer quoted
;
289 if (string
.indexOf(QUOTE
) != 0)
290 quoted
.append(QUOTE
);
292 quoted
.append(string
);
294 if (string
.lastIndexOf(QUOTE
) != (string
.getLength() - 1))
295 quoted
.append(QUOTE
);
297 return quoted
.makeStringAndClear();
300 //##########################################################
301 // Returns the system path of the executable which can either
302 // be provided via the strImageName parameter or as first
303 // element of the strArguments list.
304 // The returned path will be quoted if it contains spaces.
305 rtl::OUString
get_executable_path(
306 rtl_uString
* image_name
,
307 rtl_uString
* cmdline_args
[],
308 sal_uInt32 n_cmdline_args
,
311 rtl::OUString exe_name
;
314 exe_name
= image_name
;
315 else if (n_cmdline_args
)
316 exe_name
= rtl::OUString(cmdline_args
[0]);
318 rtl::OUString exe_url
= exe_name
;
320 osl_searchFileURL(exe_name
.pData
, NULL
, &exe_url
.pData
);
322 rtl::OUString exe_path
;
323 if (osl_File_E_None
!= osl::FileBase::getSystemPathFromFileURL(exe_url
, exe_path
))
324 return rtl::OUString();
326 if (exe_path
.indexOf(' ') != -1)
327 exe_path
= quote_string(exe_path
);
332 //##########################################################
333 rtl::OUString
get_file_extension(const rtl::OUString
& file_name
)
335 sal_Int32 index
= file_name
.lastIndexOf('.');
336 if ((index
!= -1) && ((index
+ 1) < file_name
.getLength()))
337 return file_name
.copy(index
+ 1);
339 return rtl::OUString();
342 //##########################################################
343 bool is_batch_file(const rtl::OUString
& file_name
)
345 rtl::OUString ext
= get_file_extension(file_name
);
346 return (ext
.equalsIgnoreAsciiCaseAscii("bat") ||
347 ext
.equalsIgnoreAsciiCaseAscii("cmd") ||
348 ext
.equalsIgnoreAsciiCaseAscii("btm"));
351 //##########################################################
352 rtl::OUString
get_batch_processor()
354 rtl::OUString comspec
;
355 osl_getEnvironment(ENV_COMSPEC
.pData
, &comspec
.pData
);
357 OSL_ASSERT(comspec
.getLength());
359 /* check if comspec path contains blanks and quote it if any */
360 if (comspec
.indexOf(' ') != -1)
361 comspec
= quote_string(comspec
);
366 } // namespace private
369 //#################################################
370 oslProcessError SAL_CALL
osl_executeProcess(
371 rtl_uString
*strImageName
,
372 rtl_uString
*strArguments
[],
373 sal_uInt32 nArguments
,
374 oslProcessOption Options
,
375 oslSecurity Security
,
376 rtl_uString
*strDirectory
,
377 rtl_uString
*strEnvironmentVars
[],
378 sal_uInt32 nEnvironmentVars
,
382 return osl_executeProcess_WithRedirectedIO(
395 //#################################################
396 oslProcessError SAL_CALL
osl_executeProcess_WithRedirectedIO(
397 rtl_uString
*ustrImageName
,
398 rtl_uString
*ustrArguments
[],
399 sal_uInt32 nArguments
,
400 oslProcessOption Options
,
401 oslSecurity Security
,
402 rtl_uString
*ustrDirectory
,
403 rtl_uString
*ustrEnvironmentVars
[],
404 sal_uInt32 nEnvironmentVars
,
405 oslProcess
*pProcess
,
406 oslFileHandle
*pProcessInputWrite
,
407 oslFileHandle
*pProcessOutputRead
,
408 oslFileHandle
*pProcessErrorRead
)
410 rtl::OUString exe_path
= get_executable_path(
411 ustrImageName
, ustrArguments
, nArguments
, (Options
& osl_Process_SEARCHPATH
));
413 if (0 == exe_path
.getLength())
414 return osl_Process_E_NotFound
;
416 if (pProcess
== NULL
)
417 return osl_Process_E_InvalidError
;
419 DWORD flags
= NORMAL_PRIORITY_CLASS
;
420 rtl::OUStringBuffer command_line
;
422 if (is_batch_file(exe_path
))
424 rtl::OUString batch_processor
= get_batch_processor();
426 if (batch_processor
.getLength())
428 /* cmd.exe does not work without a console window */
429 if (!(Options
& osl_Process_WAIT
) || (Options
& osl_Process_DETACHED
))
430 flags
|= CREATE_NEW_CONSOLE
;
432 command_line
.append(batch_processor
);
433 command_line
.appendAscii(" /c ");
436 // should we return here in case of error?
437 return osl_Process_E_Unknown
;
440 command_line
.append(exe_path
);
442 /* Add remaining arguments to command line. If ustrImageName is NULL
443 the first parameter is the name of the executable so we have to
444 start at 1 instead of 0 */
445 for (sal_uInt32 n
= (NULL
!= ustrImageName
) ? 0 : 1; n
< nArguments
; n
++)
447 command_line
.appendAscii(SPACE
);
449 /* Quote arguments containing blanks */
450 if (rtl::OUString(ustrArguments
[n
]).indexOf(' ') != -1)
451 command_line
.append(quote_string(ustrArguments
[n
]));
453 command_line
.append(ustrArguments
[n
]);
456 environment_container_t environment
;
457 LPVOID p_environment
= NULL
;
459 if (nEnvironmentVars
&& ustrEnvironmentVars
)
461 if (!setup_process_environment(
462 ustrEnvironmentVars
, nEnvironmentVars
, environment
))
463 return osl_Process_E_InvalidError
;
465 flags
|= CREATE_UNICODE_ENVIRONMENT
;
466 p_environment
= &environment
[0];
470 if (ustrDirectory
&& ustrDirectory
->length
&& (osl_File_E_None
!= osl::FileBase::getSystemPathFromFileURL(ustrDirectory
, cwd
)))
471 return osl_Process_E_InvalidError
;
473 LPCWSTR p_cwd
= (cwd
.getLength()) ? reinterpret_cast<LPCWSTR
>(cwd
.getStr()) : NULL
;
475 if ((Options
& osl_Process_DETACHED
) && !(flags
& CREATE_NEW_CONSOLE
))
476 flags
|= DETACHED_PROCESS
;
478 STARTUPINFO startup_info
;
479 memset(&startup_info
, 0, sizeof(STARTUPINFO
));
481 startup_info
.cb
= sizeof(STARTUPINFO
);
482 startup_info
.dwFlags
= STARTF_USESHOWWINDOW
;
483 startup_info
.lpDesktop
= L
"";
485 /* Create pipes for redirected IO */
486 HANDLE hInputRead
= NULL
;
487 HANDLE hInputWrite
= NULL
;
488 if (pProcessInputWrite
&& create_pipe(&hInputRead
, true, &hInputWrite
, false))
489 startup_info
.hStdInput
= hInputRead
;
491 HANDLE hOutputRead
= NULL
;
492 HANDLE hOutputWrite
= NULL
;
493 if (pProcessOutputRead
&& create_pipe(&hOutputRead
, false, &hOutputWrite
, true))
494 startup_info
.hStdOutput
= hOutputWrite
;
496 HANDLE hErrorRead
= NULL
;
497 HANDLE hErrorWrite
= NULL
;
498 if (pProcessErrorRead
&& create_pipe(&hErrorRead
, false, &hErrorWrite
, true))
499 startup_info
.hStdError
= hErrorWrite
;
501 bool b_inherit_handles
= false;
502 if (pProcessInputWrite
|| pProcessOutputRead
|| pProcessErrorRead
)
504 startup_info
.dwFlags
|= STARTF_USESTDHANDLES
;
505 b_inherit_handles
= true;
508 switch(Options
& (osl_Process_NORMAL
| osl_Process_HIDDEN
| osl_Process_MINIMIZED
| osl_Process_MAXIMIZED
| osl_Process_FULLSCREEN
))
510 case osl_Process_HIDDEN
:
511 startup_info
.wShowWindow
= SW_HIDE
;
512 flags
|= CREATE_NO_WINDOW
; // ignored for non-console
513 // applications; ignored on
517 case osl_Process_MINIMIZED
:
518 startup_info
.wShowWindow
= SW_MINIMIZE
;
521 case osl_Process_MAXIMIZED
:
522 case osl_Process_FULLSCREEN
:
523 startup_info
.wShowWindow
= SW_MAXIMIZE
;
527 startup_info
.wShowWindow
= SW_NORMAL
;
530 rtl::OUString cmdline
= command_line
.makeStringAndClear();
531 PROCESS_INFORMATION process_info
;
534 if ((Security
!= NULL
) && (((oslSecurityImpl
*)Security
)->m_hToken
!= NULL
))
536 bRet
= CreateProcessAsUser(
537 ((oslSecurityImpl
*)Security
)->m_hToken
,
538 NULL
, const_cast<LPTSTR
>(reinterpret_cast<LPCTSTR
>(cmdline
.getStr())), NULL
, NULL
,
539 b_inherit_handles
, flags
, p_environment
, p_cwd
,
540 &startup_info
, &process_info
);
544 bRet
= CreateProcess(
545 NULL
, const_cast<LPTSTR
>(reinterpret_cast<LPCTSTR
>(cmdline
.getStr())), NULL
, NULL
,
546 b_inherit_handles
, flags
, p_environment
, p_cwd
,
547 &startup_info
, &process_info
);
550 /* Now we can close the pipe ends that are used by the child process */
553 CloseHandle(hInputRead
);
556 CloseHandle(hOutputWrite
);
559 CloseHandle(hErrorWrite
);
563 CloseHandle(process_info
.hThread
);
565 oslProcessImpl
* pProcImpl
= reinterpret_cast<oslProcessImpl
*>(
566 rtl_allocateMemory(sizeof(oslProcessImpl
)));
568 if (pProcImpl
!= NULL
)
570 pProcImpl
->m_hProcess
= process_info
.hProcess
;
571 pProcImpl
->m_IdProcess
= process_info
.dwProcessId
;
573 *pProcess
= (oslProcess
)pProcImpl
;
575 if (Options
& osl_Process_WAIT
)
576 WaitForSingleObject(pProcImpl
->m_hProcess
, INFINITE
);
578 if (pProcessInputWrite
)
579 *pProcessInputWrite
= osl_createFileHandleFromOSHandle(hInputWrite
, osl_File_OpenFlag_Write
);
581 if (pProcessOutputRead
)
582 *pProcessOutputRead
= osl_createFileHandleFromOSHandle(hOutputRead
, osl_File_OpenFlag_Read
);
584 if (pProcessErrorRead
)
585 *pProcessErrorRead
= osl_createFileHandleFromOSHandle(hErrorRead
, osl_File_OpenFlag_Read
);
587 return osl_Process_E_None
;
591 /* if an error occured we have to close the server side pipe ends too */
594 CloseHandle(hInputWrite
);
597 CloseHandle(hOutputRead
);
600 CloseHandle(hErrorRead
);
602 return osl_Process_E_Unknown
;