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