Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 : #ifdef IOS
21 : #define CPPUNIT_PLUGIN_EXPORTED_NAME cppunitTest_osl_process
22 : #endif
23 :
24 : #include <sal/types.h>
25 : #include <cppunit/TestFixture.h>
26 : #include <cppunit/extensions/HelperMacros.h>
27 : #include <cppunit/plugin/TestPlugIn.h>
28 :
29 : #include <osl/process.h>
30 : #include <osl/file.hxx>
31 : #include <osl/thread.h>
32 : #include <rtl/ustring.hxx>
33 : #include <signal.h>
34 :
35 : #include <stdio.h>
36 : #include <stdlib.h>
37 : #include <osl/module.hxx>
38 : #include <sal/macros.h>
39 :
40 : #if defined HAVE_VALGRIND_HEADERS
41 : #include <valgrind/valgrind.h>
42 : #else
43 : #define RUNNING_ON_VALGRIND false
44 : #endif
45 :
46 : #if ( defined WNT ) // Windows
47 : # include <windows.h>
48 : # include <tchar.h>
49 : #else
50 : #include <unistd.h>
51 : #endif
52 :
53 : #include <iostream>
54 : #include <fstream>
55 : #include <vector>
56 : #include <algorithm>
57 : #include <iterator>
58 : #include <string>
59 :
60 : #ifdef UNX
61 : #if defined( MACOSX )
62 : # include <crt_externs.h>
63 : # define environ (*_NSGetEnviron())
64 : # else
65 : extern char** environ;
66 : # endif
67 : #endif
68 :
69 : #if defined(WNT)
70 : const rtl::OUString EXECUTABLE_NAME ("osl_process_child.exe");
71 : #else
72 2 : const rtl::OUString EXECUTABLE_NAME ("osl_process_child");
73 : #endif
74 :
75 : using namespace osl;
76 :
77 : using ::rtl::OUString;
78 : using ::rtl::OUStringToOString;
79 : using ::rtl::OString;
80 :
81 : /** get binary Path.
82 : */
83 6 : inline ::rtl::OUString getExecutablePath( void )
84 : {
85 6 : ::rtl::OUString dirPath;
86 6 : osl::Module::getUrlFromAddress( ( void* ) &getExecutablePath, dirPath );
87 6 : dirPath = dirPath.copy( 0, dirPath.lastIndexOf('/') );
88 6 : dirPath = dirPath.copy( 0, dirPath.lastIndexOf('/') + 1);
89 6 : dirPath += rtl::OUString("Executable");
90 6 : return dirPath;
91 : }
92 :
93 : //rtl::OUString CWD = getExecutablePath();
94 :
95 : typedef std::vector<OString> string_container_t;
96 : typedef string_container_t::const_iterator string_container_const_iter_t;
97 : typedef string_container_t::iterator string_container_iter_t;
98 :
99 56 : class exclude : public std::unary_function<OString, bool>
100 : {
101 : public:
102 :
103 4 : exclude(const string_container_t& exclude_list)
104 4 : {
105 4 : string_container_const_iter_t iter = exclude_list.begin();
106 4 : string_container_const_iter_t iter_end = exclude_list.end();
107 16 : for (/**/; iter != iter_end; ++iter)
108 12 : exclude_list_.push_back(env_var_name(*iter));
109 4 : }
110 :
111 3234 : bool operator() (const OString& env_var) const
112 : {
113 6468 : return (exclude_list_.end() !=
114 : std::find(
115 : exclude_list_.begin(),
116 : exclude_list_.end(),
117 9702 : env_var_name(env_var)));
118 : }
119 :
120 : private:
121 :
122 : // extract the name from an environment variable
123 : // that is given in the form "NAME=VALUE"
124 3246 : OString env_var_name(const OString& env_var) const
125 : {
126 : sal_Int32 pos_equal_sign =
127 3246 : env_var.indexOf('=');
128 :
129 3246 : if (-1 != pos_equal_sign)
130 3246 : return env_var.copy(0, pos_equal_sign);
131 :
132 0 : return OString();
133 : }
134 :
135 : private:
136 : string_container_t exclude_list_;
137 : };
138 :
139 : namespace
140 : {
141 8 : void tidy_container(string_container_t &env_container)
142 : {
143 : //sort them because there are no guarantees to ordering
144 8 : std::sort(env_container.begin(), env_container.end());
145 : if (RUNNING_ON_VALGRIND)
146 : {
147 : env_container.erase(
148 : std::remove_if(
149 : env_container.begin(), env_container.end(),
150 0 : [](OString const & s) {
151 0 : return s.startsWith("LD_PRELOAD=")
152 0 : || s.startsWith("VALGRIND_LIB="); }),
153 : env_container.end());
154 : }
155 8 : }
156 : }
157 :
158 : #ifdef WNT
159 : void read_parent_environment(string_container_t* env_container)
160 : {
161 : LPTSTR env = reinterpret_cast<LPTSTR>(GetEnvironmentStrings());
162 : LPTSTR p = env;
163 :
164 : while (size_t l = _tcslen(p))
165 : {
166 : env_container->push_back(OString(p));
167 : p += l + 1;
168 : }
169 : FreeEnvironmentStrings(env);
170 : tidy_container(*env_container);
171 : }
172 : #else
173 4 : void read_parent_environment(string_container_t* env_container)
174 : {
175 3232 : for (int i = 0; NULL != environ[i]; i++)
176 3228 : env_container->push_back(OString(environ[i]));
177 4 : tidy_container(*env_container);
178 4 : }
179 : #endif
180 :
181 12 : class Test_osl_executeProcess : public CppUnit::TestFixture
182 : {
183 : const OUString env_param_;
184 :
185 : OUString temp_file_url_;
186 : OUString temp_file_path_;
187 : rtl_uString* parameters_[2];
188 : int parameters_count_;
189 : OUString suCWD;
190 : OUString suExecutableFileURL;
191 :
192 : public:
193 :
194 : // ctor
195 6 : Test_osl_executeProcess() :
196 : env_param_(OUString("-env")),
197 6 : parameters_count_(2)
198 : {
199 6 : parameters_[0] = env_param_.pData;
200 6 : suCWD = getExecutablePath();
201 6 : suExecutableFileURL = suCWD;
202 6 : suExecutableFileURL += rtl::OUString("/");
203 6 : suExecutableFileURL += EXECUTABLE_NAME;
204 6 : }
205 :
206 6 : virtual void setUp() SAL_OVERRIDE
207 : {
208 6 : temp_file_path_ = create_temp_file(temp_file_url_);
209 6 : parameters_[1] = temp_file_path_.pData;
210 6 : }
211 :
212 6 : virtual void tearDown() SAL_OVERRIDE
213 : {
214 6 : osl::File::remove(temp_file_url_);
215 6 : }
216 :
217 6 : OUString create_temp_file(OUString &temp_file_url)
218 : {
219 6 : FileBase::RC rc = FileBase::createTempFile(0, 0, &temp_file_url);
220 6 : CPPUNIT_ASSERT_EQUAL_MESSAGE("createTempFile failed", FileBase::E_None, rc);
221 :
222 6 : OUString temp_file_path;
223 6 : rc = FileBase::getSystemPathFromFileURL(temp_file_url, temp_file_path);
224 6 : CPPUNIT_ASSERT_EQUAL_MESSAGE("getSystemPathFromFileURL failed", FileBase::E_None, rc);
225 :
226 6 : return temp_file_path;
227 : }
228 :
229 4 : void read_child_environment(string_container_t* env_container)
230 : {
231 : OString temp_file_name = OUStringToOString(OUString(
232 4 : parameters_[1]), osl_getThreadTextEncoding());
233 8 : std::ifstream file(temp_file_name.getStr());
234 :
235 8 : CPPUNIT_ASSERT_MESSAGE
236 : (
237 : "I/O error, cannot open child environment file",
238 : file.is_open()
239 4 : );
240 :
241 8 : std::string line;
242 4 : line.reserve(1024);
243 3242 : while (std::getline(file, line, '\0'))
244 3234 : env_container->push_back(OString(line.c_str()));
245 8 : tidy_container(*env_container);
246 4 : }
247 :
248 : // environment of the child process that was
249 : // started. The child process writes his
250 : // environment into a file
251 2 : void compare_environments()
252 : {
253 2 : string_container_t parent_env;
254 2 : read_parent_environment(&parent_env);
255 :
256 4 : string_container_t child_env;
257 2 : read_child_environment(&child_env);
258 :
259 : OString msg(
260 4 : OString::number(parent_env.size()) + "/"
261 8 : + OString::number(child_env.size()));
262 2 : auto min = std::min(parent_env.size(), child_env.size());
263 1616 : for (decltype(min) i = 0; i != min; ++i) {
264 3228 : CPPUNIT_ASSERT_EQUAL_MESSAGE(
265 1614 : msg.getStr(), parent_env[i], child_env[i]);
266 : }
267 2 : if (parent_env.size() != child_env.size()) {
268 0 : CPPUNIT_ASSERT_EQUAL_MESSAGE(
269 : (parent_env.size() >= child_env.size()
270 : ? parent_env.back() : child_env.back()).getStr(),
271 0 : parent_env.size(), child_env.size());
272 2 : }
273 2 : }
274 :
275 : // compare the equal environment parts and the
276 : // different part of the child environment
277 2 : bool compare_merged_environments(const string_container_t& different_env_vars)
278 : {
279 2 : string_container_t parent_env;
280 2 : read_parent_environment(&parent_env);
281 :
282 : #if OSL_DEBUG_LEVEL > 1
283 : for (string_container_t::const_iterator iter = parent_env.begin(), end = parent_env.end(); iter != end; ++iter)
284 : std::cerr << "initially parent env: " << *iter << std::endl;
285 : #endif
286 :
287 : //remove the environment variables that we have changed
288 : //in the child environment from the read parent environment
289 : parent_env.erase(
290 4 : std::remove_if(parent_env.begin(), parent_env.end(), exclude(different_env_vars)),
291 6 : parent_env.end());
292 :
293 : #if OSL_DEBUG_LEVEL > 1
294 : for (string_container_t::const_iterator iter = parent_env.begin(), end = parent_env.end(); iter != end; ++iter)
295 : std::cerr << "stripped parent env: " << *iter << std::endl;
296 : #endif
297 :
298 : //read the child environment and exclude the variables that
299 : //are different
300 4 : string_container_t child_env;
301 2 : read_child_environment(&child_env);
302 :
303 : #if OSL_DEBUG_LEVEL > 1
304 : for (string_container_t::const_iterator iter = child_env.begin(), end = child_env.end(); iter != end; ++iter)
305 : std::cerr << "initial child env: " << *iter << std::endl;
306 : #endif
307 : //partition the child environment into the variables that
308 : //are different to the parent environment (they come first)
309 : //and the variables that should be equal between parent
310 : //and child environment
311 : string_container_iter_t iter_logical_end =
312 2 : std::stable_partition(child_env.begin(), child_env.end(), exclude(different_env_vars));
313 :
314 4 : string_container_t different_child_env_vars(child_env.begin(), iter_logical_end);
315 2 : child_env.erase(child_env.begin(), iter_logical_end);
316 :
317 : #if OSL_DEBUG_LEVEL > 1
318 : for (string_container_t::const_iterator iter = child_env.begin(), end = child_env.end(); iter != end; ++iter)
319 : std::cerr << "stripped child env: " << *iter << std::endl;
320 : #endif
321 :
322 2 : bool common_env_size_equals = (parent_env.size() == child_env.size());
323 2 : bool common_env_content_equals = std::equal(child_env.begin(), child_env.end(), parent_env.begin());
324 :
325 : #if OSL_DEBUG_LEVEL > 1
326 : for (string_container_t::const_iterator iter = different_env_vars.begin(), end = different_env_vars.end(); iter != end; ++iter)
327 : std::cerr << "different should be: " << *iter << std::endl;
328 : #endif
329 :
330 : #if OSL_DEBUG_LEVEL > 1
331 : for (string_container_t::const_iterator iter = different_child_env_vars.begin(), end = different_child_env_vars.end(); iter != end; ++iter)
332 : std::cerr << "different are: " << *iter << std::endl;
333 : #endif
334 :
335 2 : bool different_env_size_equals = (different_child_env_vars.size() == different_env_vars.size());
336 : bool different_env_content_equals =
337 2 : std::equal(different_env_vars.begin(), different_env_vars.end(), different_child_env_vars.begin());
338 :
339 2 : return (common_env_size_equals && common_env_content_equals &&
340 6 : different_env_size_equals && different_env_content_equals);
341 : }
342 :
343 : // test that parent and child process have the
344 : // same environment when osl_executeProcess will
345 : // be called with out setting new environment
346 : // variables
347 2 : void osl_execProc_parent_equals_child_environment()
348 : {
349 : oslProcess process;
350 : oslProcessError osl_error = osl_executeProcess(
351 : suExecutableFileURL.pData,
352 : parameters_,
353 : parameters_count_,
354 : osl_Process_NORMAL,
355 : NULL,
356 : suCWD.pData,
357 : NULL,
358 : 0,
359 2 : &process);
360 :
361 4 : CPPUNIT_ASSERT_EQUAL_MESSAGE
362 : (
363 : "osl_createProcess failed",
364 : osl_Process_E_None, osl_error
365 2 : );
366 :
367 2 : osl_error = ::osl_joinProcess(process);
368 :
369 4 : CPPUNIT_ASSERT_EQUAL_MESSAGE
370 : (
371 : "osl_joinProcess returned with failure",
372 : osl_Process_E_None, osl_error
373 2 : );
374 :
375 2 : osl_freeProcessHandle(process);
376 :
377 2 : compare_environments();
378 2 : }
379 :
380 : #define ENV1 "PAT=a:\\"
381 : #define ENV2 "PATHb=b:\\"
382 : #define ENV3 "Patha=c:\\"
383 : #define ENV4 "Patha=d:\\"
384 :
385 2 : void osl_execProc_merged_child_environment()
386 : {
387 : rtl_uString* child_env[4];
388 2 : OUString env1(ENV1);
389 4 : OUString env2(ENV2);
390 4 : OUString env3(ENV3);
391 4 : OUString env4(ENV4);
392 :
393 2 : child_env[0] = env1.pData;
394 2 : child_env[1] = env2.pData;
395 2 : child_env[2] = env3.pData;
396 2 : child_env[3] = env4.pData;
397 :
398 : oslProcess process;
399 : oslProcessError osl_error = osl_executeProcess(
400 : suExecutableFileURL.pData,
401 : parameters_,
402 : parameters_count_,
403 : osl_Process_NORMAL,
404 : NULL,
405 : suCWD.pData,
406 : child_env,
407 : SAL_N_ELEMENTS(child_env),
408 2 : &process);
409 :
410 4 : CPPUNIT_ASSERT_EQUAL_MESSAGE
411 : (
412 : "osl_createProcess failed",
413 : osl_Process_E_None, osl_error
414 2 : );
415 :
416 2 : osl_error = ::osl_joinProcess(process);
417 :
418 4 : CPPUNIT_ASSERT_EQUAL_MESSAGE
419 : (
420 : "osl_joinProcess returned with failure",
421 : osl_Process_E_None, osl_error
422 2 : );
423 :
424 2 : osl_freeProcessHandle(process);
425 :
426 4 : string_container_t different_child_env_vars;
427 2 : different_child_env_vars.push_back(ENV1);
428 2 : different_child_env_vars.push_back(ENV2);
429 2 : different_child_env_vars.push_back(ENV4);
430 :
431 4 : CPPUNIT_ASSERT_MESSAGE
432 : (
433 : "osl_execProc_merged_child_environment",
434 : compare_merged_environments(different_child_env_vars)
435 4 : );
436 2 : }
437 :
438 2 : void osl_execProc_test_batch()
439 : {
440 : oslProcess process;
441 : #if defined(WNT)
442 : rtl::OUString suBatch = suCWD + rtl::OUString("/") + rtl::OUString("batch.bat");
443 : #else
444 2 : rtl::OUString suBatch = suCWD + rtl::OUString("/") + rtl::OUString("batch.sh");
445 : #endif
446 : oslProcessError osl_error = osl_executeProcess(
447 : suBatch.pData,
448 : NULL,
449 : 0,
450 : osl_Process_NORMAL,
451 : NULL,
452 : suCWD.pData,
453 : NULL,
454 : 0,
455 2 : &process);
456 :
457 4 : CPPUNIT_ASSERT_EQUAL_MESSAGE
458 : (
459 : "osl_createProcess failed",
460 : osl_Process_E_None, osl_error
461 2 : );
462 :
463 2 : osl_error = ::osl_joinProcess(process);
464 :
465 4 : CPPUNIT_ASSERT_EQUAL_MESSAGE
466 : (
467 : "osl_joinProcess returned with failure",
468 : osl_Process_E_None, osl_error
469 2 : );
470 :
471 2 : osl_freeProcessHandle(process);
472 2 : }
473 :
474 4 : CPPUNIT_TEST_SUITE(Test_osl_executeProcess);
475 : //TODO: Repair these (at least under Windows)
476 : #if !defined(WNT)
477 2 : CPPUNIT_TEST(osl_execProc_parent_equals_child_environment);
478 2 : CPPUNIT_TEST(osl_execProc_merged_child_environment);
479 : #endif
480 2 : CPPUNIT_TEST(osl_execProc_test_batch);
481 : ///TODO: Repair test (or tested function ;-) - test fails.
482 4 : CPPUNIT_TEST_SUITE_END();
483 : };
484 :
485 2 : CPPUNIT_TEST_SUITE_REGISTRATION(Test_osl_executeProcess);
486 :
487 8 : CPPUNIT_PLUGIN_IMPLEMENT();
488 :
489 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|