1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
24 #ifndef WIN32_LEAN_AND_MEAN
25 # define WIN32_LEAN_AND_MEAN
27 # pragma warning(push,1) /* disable warnings within system headers */
34 # undef WIN32_LEAN_AND_MEAN
37 #include <rtl/ustring.hxx>
38 #include <rtl/ustrbuf.hxx>
40 #include "rtl/allocator.hxx"
41 #include <osl/file.hxx>
49 //#################################################
50 extern "C" oslFileHandle SAL_CALL
osl_createFileHandleFromOSHandle( HANDLE hFile
, sal_uInt32 uFlags
);
52 //#################################################
53 const sal_Unicode NAME_VALUE_SEPARATOR
= TEXT('=');
54 const sal_Char
* SPACE
= " ";
55 const rtl::OUString
ENV_COMSPEC ("COMSPEC");
56 const rtl::OUString
QUOTE("\"");
58 namespace /* private */
60 //#################################################
61 typedef std::list
<rtl::OUString
> string_container_t
;
62 typedef string_container_t::iterator string_container_iterator_t
;
63 typedef string_container_t::const_iterator string_container_const_iterator_t
;
64 typedef std::pair
<string_container_iterator_t
, string_container_iterator_t
> iterator_pair_t
;
65 typedef std::vector
<sal_Unicode
> environment_container_t
;
67 //#################################################
68 /* Function object that compares two strings that are
69 expected to be environment variables in the form
70 "name=value". Only the 'name' part will be compared.
71 The comparison is in upper case and returns true
72 if the first of both strings is less than the
74 struct less_environment_variable
:
75 public std::binary_function
<rtl::OUString
, rtl::OUString
, bool>
77 bool operator() (const rtl::OUString
& lhs
, const rtl::OUString
& rhs
) const
79 OSL_ENSURE((lhs
.indexOf(NAME_VALUE_SEPARATOR
) > -1) && \
80 (rhs
.indexOf(NAME_VALUE_SEPARATOR
) > -1), \
81 "Malformed environment variable");
83 // Windows compares environment variables uppercase
85 return (rtl_ustr_compare_WithLength(
86 lhs
.toAsciiUpperCase().pData
->buffer
,
87 lhs
.indexOf(NAME_VALUE_SEPARATOR
),
88 rhs
.toAsciiUpperCase().pData
->buffer
,
89 rhs
.indexOf(NAME_VALUE_SEPARATOR
)) < 0);
93 //#################################################
94 /* Function object used by for_each algorithm to
95 calculate the sum of the length of all strings
96 in a string container. */
97 class sum_of_string_lengths
100 //--------------------------------
101 sum_of_string_lengths() : sum_(0) {}
103 //--------------------------------
104 void operator() (const rtl::OUString
& string
)
106 OSL_ASSERT(string
.getLength());
108 // always include the terminating '\0'
109 if (string
.getLength())
110 sum_
+= string
.getLength() + 1;
113 //--------------------------------
114 operator size_t () const
122 //#################################################
123 inline size_t calc_sum_of_string_lengths(const string_container_t
& string_cont
)
125 return std::for_each(
126 string_cont
.begin(), string_cont
.end(), sum_of_string_lengths());
129 //#################################################
130 void read_environment(/*out*/ string_container_t
* environment
)
132 // GetEnvironmentStrings returns a sorted list, Windows
133 // sorts environment variables upper case
134 LPTSTR env
= reinterpret_cast<LPTSTR
>(GetEnvironmentStrings());
137 while (size_t l
= _tcslen(p
))
139 environment
->push_back(reinterpret_cast<const sal_Unicode
*>(p
));
142 FreeEnvironmentStrings(env
);
145 //#################################################
146 /* the environment list must be sorted, new values
147 should either replace existing ones or should be
148 added to the list, environment variables will
149 be handled case-insensitive */
150 bool create_merged_environment(
151 rtl_uString
* env_vars
[],
152 sal_uInt32 env_vars_count
,
153 /*in|out*/ string_container_t
* merged_env
)
155 OSL_ASSERT(env_vars
&& env_vars_count
> 0 && merged_env
);
157 read_environment(merged_env
);
159 for (sal_uInt32 i
= 0; i
< env_vars_count
; i
++)
161 rtl::OUString env_var
= rtl::OUString(env_vars
[i
]);
163 if (env_var
.getLength() == 0)
166 iterator_pair_t iter_pair
= std::equal_range(
170 less_environment_variable());
172 if (env_var
.indexOf(NAME_VALUE_SEPARATOR
) == -1)
174 merged_env
->erase(iter_pair
.first
, iter_pair
.second
);
178 if (iter_pair
.first
!= iter_pair
.second
) // found
179 *iter_pair
.first
= env_var
;
181 merged_env
->insert(iter_pair
.first
, env_var
);
187 //#################################################
188 /* Create a merged environment */
189 bool setup_process_environment(
190 rtl_uString
* environment_vars
[],
191 sal_uInt32 n_environment_vars
,
192 /*in|out*/ environment_container_t
& environment
)
194 string_container_t merged_env
;
195 if (!create_merged_environment(environment_vars
, n_environment_vars
, &merged_env
))
198 // allocate enough space for the '\0'-separated environment strings and
200 environment
.resize(calc_sum_of_string_lengths(merged_env
) + 1);
202 string_container_const_iterator_t iter
= merged_env
.begin();
203 string_container_const_iterator_t iter_end
= merged_env
.end();
206 for (/**/; iter
!= iter_end
; ++iter
)
208 rtl::OUString envv
= *iter
;
210 OSL_ASSERT(envv
.getLength());
212 sal_uInt32 n
= envv
.getLength() + 1; // copy the final '\0', too
214 reinterpret_cast<void*>(&environment
[pos
]),
215 reinterpret_cast<const void*>(envv
.getStr()),
216 n
* sizeof(sal_Unicode
));
219 environment
[pos
] = 0; // append a final '\0'
224 //##########################################################
225 /* In contrast to the Win32 API function CreatePipe with
226 this function the caller is able to determine separately
227 which handle of the pipe is inheritable. */
230 bool b_read_pipe_inheritable
,
231 PHANDLE p_write_pipe
,
232 bool b_write_pipe_inheritable
,
233 LPVOID p_security_descriptor
= NULL
,
236 SECURITY_ATTRIBUTES sa
;
237 sa
.nLength
= sizeof(SECURITY_ATTRIBUTES
);
238 sa
.lpSecurityDescriptor
= p_security_descriptor
;
239 sa
.bInheritHandle
= b_read_pipe_inheritable
|| b_write_pipe_inheritable
;
244 if (!b_read_pipe_inheritable
&& b_write_pipe_inheritable
)
246 bRet
= CreatePipe(&hTemp
, p_write_pipe
, &sa
, pipe_size
);
248 if (bRet
&& !DuplicateHandle(GetCurrentProcess(), hTemp
,
249 GetCurrentProcess(), p_read_pipe
, 0, FALSE
,
250 DUPLICATE_CLOSE_SOURCE
| DUPLICATE_SAME_ACCESS
))
253 CloseHandle(*p_read_pipe
);
257 else if (b_read_pipe_inheritable
&& !b_write_pipe_inheritable
)
259 bRet
= CreatePipe(p_read_pipe
, &hTemp
, &sa
, pipe_size
);
261 if (bRet
&& !DuplicateHandle(GetCurrentProcess(), hTemp
,
262 GetCurrentProcess(), p_write_pipe
, 0, FALSE
,
263 DUPLICATE_CLOSE_SOURCE
| DUPLICATE_SAME_ACCESS
))
266 CloseHandle(*p_write_pipe
);
272 bRet
= CreatePipe(p_read_pipe
, p_write_pipe
, &sa
, pipe_size
);
277 //#########################################################
278 // Add a quote sign to the start and the end of a string
279 // if not already present
280 rtl::OUString
quote_string(const rtl::OUString
& string
)
282 rtl::OUStringBuffer quoted
;
283 if (string
.indexOf(QUOTE
) != 0)
284 quoted
.append(QUOTE
);
286 quoted
.append(string
);
288 if (string
.lastIndexOf(QUOTE
) != (string
.getLength() - 1))
289 quoted
.append(QUOTE
);
291 return quoted
.makeStringAndClear();
294 //The parameter path must be a system path. If it is longer than 260 characters
295 //then it is shortened using the GetShortPathName function. This function only
296 //works if the path exists. Because "path" can be the path to an executable, it
297 //may not have the file extension ".exe". However, if the file on disk has the
298 //".exe" extension, then the function will fail. In this case a second attempt
299 //is started by adding the parameter "extension" to "path".
300 rtl::OUString
getShortPath(rtl::OUString
const & path
, rtl::OUString
const & extension
)
302 rtl::OUString
ret(path
);
303 if (path
.getLength() > 260)
305 std::vector
<sal_Unicode
> vec(path
.getLength() + 1);
306 //GetShortPathNameW only works if the file can be found!
307 const DWORD len
= GetShortPathNameW(
308 reinterpret_cast<LPCWSTR
>(path
.getStr()), reinterpret_cast<LPWSTR
>(&vec
[0]), path
.getLength() + 1);
310 if (!len
&& GetLastError() == ERROR_FILE_NOT_FOUND
311 && extension
.getLength())
313 const rtl::OUString
extPath(path
+ extension
);
314 std::vector
<sal_Unicode
> vec2(
315 extPath
.getLength() + 1);
316 const DWORD len2
= GetShortPathNameW(
317 reinterpret_cast<LPCWSTR
>(extPath
.getStr()), reinterpret_cast<LPWSTR
>(&vec2
[0]), extPath
.getLength() + 1);
318 ret
= rtl::OUString(&vec2
[0], len2
);
322 ret
= rtl::OUString(&vec
[0], len
);
327 //##########################################################
328 // Returns the system path of the executable which can either
329 // be provided via the strImageName parameter or as first
330 // element of the strArguments list.
331 // The returned path will be quoted if it contains spaces.
332 rtl::OUString
get_executable_path(
333 rtl_uString
* image_name
,
334 rtl_uString
* cmdline_args
[],
335 sal_uInt32 n_cmdline_args
,
338 rtl::OUString exe_name
;
341 exe_name
= image_name
;
342 else if (n_cmdline_args
)
343 exe_name
= rtl::OUString(cmdline_args
[0]);
345 rtl::OUString exe_url
= exe_name
;
347 osl_searchFileURL(exe_name
.pData
, NULL
, &exe_url
.pData
);
349 rtl::OUString exe_path
;
350 if (osl::FileBase::E_None
!= osl::FileBase::getSystemPathFromFileURL(exe_url
, exe_path
))
351 return rtl::OUString();
353 exe_path
= getShortPath(exe_path
, rtl::OUString(".exe"));
355 if (exe_path
.indexOf(' ') != -1)
356 exe_path
= quote_string(exe_path
);
361 //##########################################################
362 rtl::OUString
get_file_extension(const rtl::OUString
& file_name
)
364 sal_Int32 index
= file_name
.lastIndexOf('.');
365 if ((index
!= -1) && ((index
+ 1) < file_name
.getLength()))
366 return file_name
.copy(index
+ 1);
368 return rtl::OUString();
371 //##########################################################
372 bool is_batch_file(const rtl::OUString
& file_name
)
374 rtl::OUString ext
= get_file_extension(file_name
);
375 return (ext
.equalsIgnoreAsciiCaseAsciiL(RTL_CONSTASCII_STRINGPARAM("bat")) ||
376 ext
.equalsIgnoreAsciiCaseAsciiL(RTL_CONSTASCII_STRINGPARAM("cmd")) ||
377 ext
.equalsIgnoreAsciiCaseAsciiL(RTL_CONSTASCII_STRINGPARAM("btm")));
380 //##########################################################
381 rtl::OUString
get_batch_processor()
383 rtl::OUString comspec
;
384 osl_getEnvironment(ENV_COMSPEC
.pData
, &comspec
.pData
);
386 OSL_ASSERT(comspec
.getLength());
388 /* check if comspec path contains blanks and quote it if any */
389 if (comspec
.indexOf(' ') != -1)
390 comspec
= quote_string(comspec
);
395 } // namespace private
398 //#################################################
399 oslProcessError SAL_CALL
osl_executeProcess(
400 rtl_uString
*strImageName
,
401 rtl_uString
*strArguments
[],
402 sal_uInt32 nArguments
,
403 oslProcessOption Options
,
404 oslSecurity Security
,
405 rtl_uString
*strDirectory
,
406 rtl_uString
*strEnvironmentVars
[],
407 sal_uInt32 nEnvironmentVars
,
411 return osl_executeProcess_WithRedirectedIO(
424 //#################################################
425 oslProcessError SAL_CALL
osl_executeProcess_WithRedirectedIO(
426 rtl_uString
*ustrImageName
,
427 rtl_uString
*ustrArguments
[],
428 sal_uInt32 nArguments
,
429 oslProcessOption Options
,
430 oslSecurity Security
,
431 rtl_uString
*ustrDirectory
,
432 rtl_uString
*ustrEnvironmentVars
[],
433 sal_uInt32 nEnvironmentVars
,
434 oslProcess
*pProcess
,
435 oslFileHandle
*pProcessInputWrite
,
436 oslFileHandle
*pProcessOutputRead
,
437 oslFileHandle
*pProcessErrorRead
)
439 rtl::OUString exe_path
= get_executable_path(
440 ustrImageName
, ustrArguments
, nArguments
, (Options
& osl_Process_SEARCHPATH
));
442 if (0 == exe_path
.getLength())
443 return osl_Process_E_NotFound
;
445 if (pProcess
== NULL
)
446 return osl_Process_E_InvalidError
;
448 DWORD flags
= NORMAL_PRIORITY_CLASS
;
449 rtl::OUStringBuffer command_line
;
451 if (is_batch_file(exe_path
))
453 rtl::OUString batch_processor
= get_batch_processor();
455 if (batch_processor
.getLength())
457 /* cmd.exe does not work without a console window */
458 if (!(Options
& osl_Process_WAIT
) || (Options
& osl_Process_DETACHED
))
459 flags
|= CREATE_NEW_CONSOLE
;
461 command_line
.append(batch_processor
);
462 command_line
.appendAscii(" /c ");
465 // should we return here in case of error?
466 return osl_Process_E_Unknown
;
469 command_line
.append(exe_path
);
471 /* Add remaining arguments to command line. If ustrImageName is NULL
472 the first parameter is the name of the executable so we have to
473 start at 1 instead of 0 */
474 for (sal_uInt32 n
= (NULL
!= ustrImageName
) ? 0 : 1; n
< nArguments
; n
++)
476 command_line
.appendAscii(SPACE
);
478 /* Quote arguments containing blanks */
479 if (rtl::OUString(ustrArguments
[n
]).indexOf(' ') != -1)
480 command_line
.append(quote_string(ustrArguments
[n
]));
482 command_line
.append(ustrArguments
[n
]);
485 environment_container_t environment
;
486 LPVOID p_environment
= NULL
;
488 if (nEnvironmentVars
&& ustrEnvironmentVars
)
490 if (!setup_process_environment(
491 ustrEnvironmentVars
, nEnvironmentVars
, environment
))
492 return osl_Process_E_InvalidError
;
494 flags
|= CREATE_UNICODE_ENVIRONMENT
;
495 p_environment
= &environment
[0];
499 if (ustrDirectory
&& ustrDirectory
->length
&& (osl::FileBase::E_None
!= osl::FileBase::getSystemPathFromFileURL(ustrDirectory
, cwd
)))
500 return osl_Process_E_InvalidError
;
502 LPCWSTR p_cwd
= (cwd
.getLength()) ? reinterpret_cast<LPCWSTR
>(cwd
.getStr()) : NULL
;
504 if ((Options
& osl_Process_DETACHED
) && !(flags
& CREATE_NEW_CONSOLE
))
505 flags
|= DETACHED_PROCESS
;
507 STARTUPINFO startup_info
;
508 memset(&startup_info
, 0, sizeof(STARTUPINFO
));
510 startup_info
.cb
= sizeof(STARTUPINFO
);
511 startup_info
.dwFlags
= STARTF_USESHOWWINDOW
;
512 startup_info
.lpDesktop
= const_cast<LPWSTR
>(L
"");
514 /* Create pipes for redirected IO */
515 HANDLE hInputRead
= NULL
;
516 HANDLE hInputWrite
= NULL
;
517 if (pProcessInputWrite
&& create_pipe(&hInputRead
, true, &hInputWrite
, false))
518 startup_info
.hStdInput
= hInputRead
;
520 HANDLE hOutputRead
= NULL
;
521 HANDLE hOutputWrite
= NULL
;
522 if (pProcessOutputRead
&& create_pipe(&hOutputRead
, false, &hOutputWrite
, true))
523 startup_info
.hStdOutput
= hOutputWrite
;
525 HANDLE hErrorRead
= NULL
;
526 HANDLE hErrorWrite
= NULL
;
527 if (pProcessErrorRead
&& create_pipe(&hErrorRead
, false, &hErrorWrite
, true))
528 startup_info
.hStdError
= hErrorWrite
;
530 bool b_inherit_handles
= false;
531 if (pProcessInputWrite
|| pProcessOutputRead
|| pProcessErrorRead
)
533 startup_info
.dwFlags
|= STARTF_USESTDHANDLES
;
534 b_inherit_handles
= true;
537 switch(Options
& (osl_Process_NORMAL
| osl_Process_HIDDEN
| osl_Process_MINIMIZED
| osl_Process_MAXIMIZED
| osl_Process_FULLSCREEN
))
539 case osl_Process_HIDDEN
:
540 startup_info
.wShowWindow
= SW_HIDE
;
541 flags
|= CREATE_NO_WINDOW
; // ignored for non-console
542 // applications; ignored on
546 case osl_Process_MINIMIZED
:
547 startup_info
.wShowWindow
= SW_MINIMIZE
;
550 case osl_Process_MAXIMIZED
:
551 case osl_Process_FULLSCREEN
:
552 startup_info
.wShowWindow
= SW_MAXIMIZE
;
556 startup_info
.wShowWindow
= SW_NORMAL
;
559 rtl::OUString cmdline
= command_line
.makeStringAndClear();
560 PROCESS_INFORMATION process_info
;
563 if ((Security
!= NULL
) && (((oslSecurityImpl
*)Security
)->m_hToken
!= NULL
))
565 bRet
= CreateProcessAsUser(
566 ((oslSecurityImpl
*)Security
)->m_hToken
,
567 NULL
, const_cast<LPTSTR
>(reinterpret_cast<LPCTSTR
>(cmdline
.getStr())), NULL
, NULL
,
568 b_inherit_handles
, flags
, p_environment
, p_cwd
,
569 &startup_info
, &process_info
);
573 bRet
= CreateProcess(
574 NULL
, const_cast<LPTSTR
>(reinterpret_cast<LPCTSTR
>(cmdline
.getStr())), NULL
, NULL
,
575 b_inherit_handles
, flags
, p_environment
, p_cwd
,
576 &startup_info
, &process_info
);
579 /* Now we can close the pipe ends that are used by the child process */
582 CloseHandle(hInputRead
);
585 CloseHandle(hOutputWrite
);
588 CloseHandle(hErrorWrite
);
592 CloseHandle(process_info
.hThread
);
594 oslProcessImpl
* pProcImpl
= reinterpret_cast<oslProcessImpl
*>(
595 rtl_allocateMemory(sizeof(oslProcessImpl
)));
597 if (pProcImpl
!= NULL
)
599 pProcImpl
->m_hProcess
= process_info
.hProcess
;
600 pProcImpl
->m_IdProcess
= process_info
.dwProcessId
;
602 *pProcess
= (oslProcess
)pProcImpl
;
604 if (Options
& osl_Process_WAIT
)
605 WaitForSingleObject(pProcImpl
->m_hProcess
, INFINITE
);
607 if (pProcessInputWrite
)
608 *pProcessInputWrite
= osl_createFileHandleFromOSHandle(hInputWrite
, osl_File_OpenFlag_Write
);
610 if (pProcessOutputRead
)
611 *pProcessOutputRead
= osl_createFileHandleFromOSHandle(hOutputRead
, osl_File_OpenFlag_Read
);
613 if (pProcessErrorRead
)
614 *pProcessErrorRead
= osl_createFileHandleFromOSHandle(hErrorRead
, osl_File_OpenFlag_Read
);
616 return osl_Process_E_None
;
620 /* if an error occurred we have to close the server side pipe ends too */
623 CloseHandle(hInputWrite
);
626 CloseHandle(hOutputRead
);
629 CloseHandle(hErrorRead
);
631 return osl_Process_E_Unknown
;
634 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */