xref: /trunk/main/sal/osl/w32/procimpl.cxx (revision cdf0e10c)
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