xref: /AOO41X/main/sal/osl/w32/procimpl.cxx (revision 8809db7a87f97847b57a57f4cd2b0104b2b83182)
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 occured 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