Merge lp:~raof/mir/xserver-spawner into lp:mir
- xserver-spawner
- Merge into development-branch
Status: | Superseded |
---|---|
Proposed branch: | lp:~raof/mir/xserver-spawner |
Merge into: | lp:mir |
Diff against target: |
1684 lines (+1244/-38) 32 files modified
.bzrignore (+1/-0) .clang-format (+1/-1) include/server/mir/default_server_configuration.h (+11/-0) include/server/mir/process/handle.h (+45/-0) include/server/mir/process/spawner.h (+83/-0) include/server/mir/xserver/null_server_spawner.h (+25/-0) include/server/mir/xserver/xserver_launcher.h (+59/-0) include/shared/mir/pipe.h (+10/-6) src/server/CMakeLists.txt (+5/-0) src/server/default_server_configuration.cpp (+6/-0) src/server/process/CMakeLists.txt (+25/-0) src/server/process/fork_spawner.cpp (+165/-0) src/server/process/fork_spawner.h (+44/-0) src/server/xserver/CMakeLists.txt (+12/-0) src/server/xserver/global_socket_listening_server_spawner.cpp (+71/-0) src/server/xserver/global_socket_listening_server_spawner.h (+52/-0) src/server/xserver/null_server_spawner.cpp (+34/-0) src/shared/CMakeLists.txt (+1/-0) src/shared/pipe/CMakeLists.txt (+19/-0) src/shared/pipe/pipe.cpp (+40/-20) tests/CMakeLists.txt (+1/-0) tests/acceptance-tests/CMakeLists.txt (+3/-0) tests/acceptance-tests/test_display_configuration.cpp (+3/-2) tests/acceptance-tests/test_subprocess.cpp (+166/-0) tests/acceptance-tests/test_xserver_spawner.cpp (+73/-0) tests/integration-tests/test_display_server_main_loop_events.cpp (+3/-3) tests/mir_test/CMakeLists.txt (+0/-1) tests/unit-tests/CMakeLists.txt (+3/-0) tests/unit-tests/test_asio_main_loop.cpp (+5/-5) tests/unit-tests/test_fork_spawner.cpp (+64/-0) tests/unit-tests/xserver/CMakeLists.txt (+8/-0) tests/unit-tests/xserver/test_xserver_spawner.cpp (+206/-0) |
To merge this branch: | bzr merge lp:~raof/mir/xserver-spawner |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Daniel van Vugt | Needs Fixing | ||
Review via email: mp+203474@code.launchpad.net |
This proposal has been superseded by a proposal from 2014-01-28.
Commit message
A first cut at the infrastructure required for seamless nesting of X11 clients.
In order to do this we need to be able to spawn an Xorg server, wait until it's
ready to accept connections, connect a Mir X11 client to act as a bridging WM,
and *then* offer the X11 socket to clients.
This branch accomplishes the first two requirements.
Description of the change
A first cut at the infrastructure required for seamless nesting of X11 clients.
In order to do this we need to be able to spawn an Xorg server, wait until it's
ready to accept connections, connect a Mir X11 client to act as a bridging WM,
and *then* offer the X11 socket to clients.
This branch accomplishes the first two requirements.
I'm proposing it now because it's a reasonable checkpoint and I need to go and work on something else.
Unmerged revisions
- 1349. By Chris Halse Rogers
-
clang-format: We always split constructor initialisers on new lines
- 1348. By Chris Halse Rogers
-
Disable X11ClientConnects acceptance test.
This needs a bit more fiddling - it all roughly works, but in the acceptance
test framework we don't have a real graphics card, so XMir currently fails.Needs at least an xorg.conf specifying the dummy graphics driver
- 1347. By Chris Halse Rogers
-
Get the Xserver to connect to a Mir socket
- 1346. By Chris Halse Rogers
-
Update for process::Spawner API change
- 1345. By Chris Halse Rogers
-
Merged subprocess-wrapper into xserver-spawner.
- 1344. By Chris Halse Rogers
-
X::ServerSpawne
r::create_ server must take a shared_ ptr<process: :Spawner> create_server does things asynchronously, so it has to take shared ownership
of the process::Spawner it's using - 1343. By Chris Halse Rogers
-
Rejigger X::ServerSpawner API.
spawn_server() now returns a future<
ServerHandle> that will become a present ServerHandle once
the server is ready, rather than returning a ServerHandle now that has methods that will only
work in the future. - 1342. By Chris Halse Rogers
-
Implement -displayfd handling for GlobalSocketLis
teningServerSpa wner. client_
connection_ string now returns a string that should be associated with an Xorg server that's ready to accept connections - 1341. By Chris Halse Rogers
-
Merge process::Spawner work required to do Xserver startup properly
- 1340. By Chris Halse Rogers
-
More basic unittests for GlobalSocketLis
teningServerSpa wner
Preview Diff
1 | === added file '.bzrignore' |
2 | --- .bzrignore 1970-01-01 00:00:00 +0000 |
3 | +++ .bzrignore 2014-01-28 06:34:09 +0000 |
4 | @@ -0,0 +1,1 @@ |
5 | +build |
6 | |
7 | === modified file '.clang-format' |
8 | --- .clang-format 2014-01-10 15:48:08 +0000 |
9 | +++ .clang-format 2014-01-28 06:34:09 +0000 |
10 | @@ -14,7 +14,7 @@ |
11 | BreakConstructorInitializersBeforeComma: false |
12 | BinPackParameters: true |
13 | ColumnLimit: 120 |
14 | -ConstructorInitializerAllOnOneLineOrOnePerLine: false |
15 | +ConstructorInitializerAllOnOneLineOrOnePerLine: true |
16 | DerivePointerBinding: true |
17 | ExperimentalAutoDetectBinPacking: true |
18 | IndentCaseLabels: false |
19 | |
20 | === modified file 'include/server/mir/default_server_configuration.h' |
21 | --- include/server/mir/default_server_configuration.h 2014-01-21 15:29:52 +0000 |
22 | +++ include/server/mir/default_server_configuration.h 2014-01-28 06:34:09 +0000 |
23 | @@ -113,6 +113,11 @@ |
24 | class Logger; |
25 | } |
26 | |
27 | +namespace X |
28 | +{ |
29 | +class ServerSpawner; |
30 | +} |
31 | + |
32 | class DefaultServerConfiguration : public virtual ServerConfiguration, DefaultConfigurationOptions |
33 | { |
34 | public: |
35 | @@ -232,6 +237,12 @@ |
36 | |
37 | virtual std::shared_ptr<time::Clock> the_clock(); |
38 | |
39 | + /** @name X11 server integration - customization |
40 | + * configurable interfaces for integration with legacy X11 apps |
41 | + * @{ */ |
42 | + virtual std::shared_ptr<X::ServerSpawner> the_xserver_spawner(); |
43 | + /** @} */ |
44 | + |
45 | protected: |
46 | using DefaultConfigurationOptions::the_options; |
47 | using DefaultConfigurationOptions::add_options; |
48 | |
49 | === added directory 'include/server/mir/process' |
50 | === added file 'include/server/mir/process/handle.h' |
51 | --- include/server/mir/process/handle.h 1970-01-01 00:00:00 +0000 |
52 | +++ include/server/mir/process/handle.h 2014-01-28 06:34:09 +0000 |
53 | @@ -0,0 +1,45 @@ |
54 | +/* |
55 | + * Copyright © 2014 Canonical Ltd. |
56 | + * |
57 | + * This program is free software: you can redistribute it and/or modify it |
58 | + * under the terms of the GNU General Public License version 3, |
59 | + * as published by the Free Software Foundation. |
60 | + * |
61 | + * This program is distributed in the hope that it will be useful, |
62 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
63 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
64 | + * GNU General Public License for more details. |
65 | + * |
66 | + * You should have received a copy of the GNU General Public License |
67 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
68 | + * |
69 | + * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> |
70 | + */ |
71 | + |
72 | +#ifndef MIR_PROCESS_HANDLE_H_ |
73 | +#define MIR_PROCESS_HANDLE_H_ |
74 | + |
75 | +#include <sys/types.h> |
76 | + |
77 | +namespace mir |
78 | +{ |
79 | +namespace process |
80 | +{ |
81 | +class Handle |
82 | +{ |
83 | +public: |
84 | + enum Status { |
85 | + NotStarted, |
86 | + Running, |
87 | + Stopped |
88 | + }; |
89 | + |
90 | + virtual ~Handle() = default; |
91 | + |
92 | + virtual Status status() const = 0; |
93 | + virtual pid_t pid() const = 0; |
94 | +}; |
95 | + |
96 | +} |
97 | +} |
98 | +#endif // MIR_PROCESS_HANDLE_H_ |
99 | |
100 | === added file 'include/server/mir/process/spawner.h' |
101 | --- include/server/mir/process/spawner.h 1970-01-01 00:00:00 +0000 |
102 | +++ include/server/mir/process/spawner.h 2014-01-28 06:34:09 +0000 |
103 | @@ -0,0 +1,83 @@ |
104 | +/* |
105 | + * Copyright © 2014 Canonical Ltd. |
106 | + * |
107 | + * This program is free software: you can redistribute it and/or modify it |
108 | + * under the terms of the GNU General Public License version 3, |
109 | + * as published by the Free Software Foundation. |
110 | + * |
111 | + * This program is distributed in the hope that it will be useful, |
112 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
113 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
114 | + * GNU General Public License for more details. |
115 | + * |
116 | + * You should have received a copy of the GNU General Public License |
117 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
118 | + * |
119 | + * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> |
120 | + */ |
121 | + |
122 | +#ifndef MIR_PROCESS_SPAWNER_H_ |
123 | +#define MIR_PROCESS_SPAWNER_H_ |
124 | + |
125 | +#include <memory> |
126 | +#include <future> |
127 | +#include <initializer_list> |
128 | + |
129 | +namespace mir |
130 | +{ |
131 | +namespace process |
132 | +{ |
133 | + |
134 | +class Handle; |
135 | + |
136 | +class Spawner |
137 | +{ |
138 | +public: |
139 | + virtual ~Spawner() = default; |
140 | + /** |
141 | + * \brief Run a binary, respecting $PATH |
142 | + * \note All open file descriptors other than stdin, stdout, and stderr are closed before |
143 | + * running the binary. |
144 | + * \param [in] binary_name The name of the binary to run, such as “Xorg” |
145 | + * \return A future that will contain the handle to the spawned process. |
146 | + * The future will be populated after the process has been exec()d, |
147 | + * or the call to exec() has failed. |
148 | + * If spawning the binary fails the future will contain a |
149 | + * std::runtime_error. |
150 | + */ |
151 | + virtual std::future<std::unique_ptr<Handle>> run_from_path(char const* binary_name) const = 0; |
152 | + /** |
153 | + * \brief Run a binary with arguments, respecting $PATH |
154 | + * \note All open file descriptors other than stdin, stdout, and stderr are closed before |
155 | + * running the binary. |
156 | + * \param [in] binary_name The name of the binary to run, such as “Xorg” |
157 | + * \param [in] args The arguments to be passed to the binary. No processing is done on |
158 | + * these strings; each one will appear verbatim as a separate char* in |
159 | + * the binary's argv[]. |
160 | + * \return A future that will contain the handle to the spawned process. |
161 | + * The future will be populated after the process has been exec()d, |
162 | + * or the call to exec() has failed. |
163 | + * If spawning the binary fails the future will contain a |
164 | + * std::runtime_error. |
165 | + */ |
166 | + virtual std::future<std::unique_ptr<Handle>> run_from_path(char const* binary_name, std::initializer_list<char const*> args) const = 0; |
167 | + /** |
168 | + * \brief Run a binary, respecting $PATH |
169 | + * \note All open file descriptors other than stdin, stdout, stderr, and the fds listed in |
170 | + * fds are closed before running the binary. |
171 | + * \param [in] binary_name The name of the binary to run, such as “Xorg” |
172 | + * \param [in] args The arguments to be passed to the binary. No processing is done on |
173 | + * these strings; each one will appear verbatim as a separate char* in |
174 | + * the binary's argv[]. |
175 | + * \param [in] fds The set of fds that will remain available to the child process. |
176 | + * \return A future that will contain the handle to the spawned process. |
177 | + * The future will be populated after the process has been exec()d, |
178 | + * or the call to exec() has failed. |
179 | + * If spawning the binary fails the future will contain a |
180 | + * std::runtime_error. |
181 | + */ |
182 | + virtual std::future<std::unique_ptr<Handle>> run_from_path(char const* binary_name, std::initializer_list<char const*> args, std::initializer_list<int> fds) const = 0; |
183 | +}; |
184 | +} |
185 | +} |
186 | +#endif // MIR_PROCESS_SPAWNER_H_ |
187 | |
188 | === added directory 'include/server/mir/xserver' |
189 | === added file 'include/server/mir/xserver/null_server_spawner.h' |
190 | --- include/server/mir/xserver/null_server_spawner.h 1970-01-01 00:00:00 +0000 |
191 | +++ include/server/mir/xserver/null_server_spawner.h 2014-01-28 06:34:09 +0000 |
192 | @@ -0,0 +1,25 @@ |
193 | +#ifndef MIR_X_NULL_SERVER_SPAWNER_H_ |
194 | +#define MIR_X_NULL_SERVER_SPAWNER_H_ |
195 | + |
196 | +#include <mir/xserver/xserver_launcher.h> |
197 | + |
198 | +namespace mir |
199 | +{ |
200 | +namespace X |
201 | +{ |
202 | +class NullServerContext : public ServerContext |
203 | +{ |
204 | +public: |
205 | + char const* client_connection_string() override; |
206 | +}; |
207 | + |
208 | +class NullServerSpawner : public ServerSpawner |
209 | +{ |
210 | +public: |
211 | + std::future<std::unique_ptr<ServerContext>> create_server(std::shared_ptr<process::Spawner> const& spawner, std::shared_ptr<mir::frontend::Connector> const& connector) override; |
212 | +}; |
213 | + |
214 | +} |
215 | +} |
216 | + |
217 | +#endif // MIR_X_NULL_SERVER_SPAWNER_H_ |
218 | |
219 | === added file 'include/server/mir/xserver/xserver_launcher.h' |
220 | --- include/server/mir/xserver/xserver_launcher.h 1970-01-01 00:00:00 +0000 |
221 | +++ include/server/mir/xserver/xserver_launcher.h 2014-01-28 06:34:09 +0000 |
222 | @@ -0,0 +1,59 @@ |
223 | +/* |
224 | + * Copyright © 2014 Canonical Ltd. |
225 | + * |
226 | + * This program is free software: you can redistribute it and/or modify |
227 | + * it under the terms of the GNU General Public License version 3 as |
228 | + * published by the Free Software Foundation. |
229 | + * |
230 | + * This program is distributed in the hope that it will be useful, |
231 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
232 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
233 | + * GNU General Public License for more details. |
234 | + * |
235 | + * You should have received a copy of the GNU General Public License |
236 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
237 | + * |
238 | + * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> |
239 | + */ |
240 | + |
241 | +#ifndef MIR_X_XSERVER_LAUNCHER_H_ |
242 | +#define MIR_X_XSERVER_LAUNCHER_H_ |
243 | + |
244 | +#include <future> |
245 | +#include <memory> |
246 | + |
247 | +#include "mir/process/spawner.h" |
248 | +#include "mir/frontend/connector.h" |
249 | + |
250 | +namespace mir |
251 | +{ |
252 | +namespace X |
253 | +{ |
254 | + |
255 | +class ServerContext |
256 | +{ |
257 | +public: |
258 | + virtual ~ServerContext() = default; |
259 | + |
260 | + /** |
261 | + * \brief Get the XLib connection string to connect to this server |
262 | + * |
263 | + * This string can be passed by the client to XOpenDisplay to connect |
264 | + * to this server instance, or set in the DISPLAY environment variable |
265 | + * to be used as the default display. |
266 | + */ |
267 | + virtual char const* client_connection_string() = 0; |
268 | +}; |
269 | + |
270 | +class ServerSpawner |
271 | +{ |
272 | +public: |
273 | + virtual ~ServerSpawner() = default; |
274 | + |
275 | + virtual std::future<std::unique_ptr<ServerContext>> create_server(std::shared_ptr<mir::process::Spawner> const& spawner, |
276 | + std::shared_ptr<mir::frontend::Connector> const& connector) = 0; |
277 | +}; |
278 | +} |
279 | +} |
280 | + |
281 | +#endif // MIR_X_XSERVER_LAUNCHER_H_ |
282 | |
283 | === renamed file 'include/test/mir_test/pipe.h' => 'include/shared/mir/pipe.h' |
284 | --- include/test/mir_test/pipe.h 2013-07-03 09:54:10 +0000 |
285 | +++ include/shared/mir/pipe.h 2014-01-28 06:34:09 +0000 |
286 | @@ -1,16 +1,16 @@ |
287 | /* |
288 | * Copyright © 2013 Canonical Ltd. |
289 | * |
290 | - * This program is free software: you can redistribute it and/or modify it |
291 | - * under the terms of the GNU General Public License version 3, |
292 | - * as published by the Free Software Foundation. |
293 | + * This program is free software: you can redistribute it and/or modify |
294 | + * it under the terms of the GNU Lesser General Public License version 3 as |
295 | + * published by the Free Software Foundation. |
296 | * |
297 | * This program is distributed in the hope that it will be useful, |
298 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
299 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
300 | - * GNU General Public License for more details. |
301 | + * GNU Lesser General Public License for more details. |
302 | * |
303 | - * You should have received a copy of the GNU General Public License |
304 | + * You should have received a copy of the GNU Lesser General Public License |
305 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
306 | * |
307 | * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com> |
308 | @@ -21,18 +21,22 @@ |
309 | |
310 | namespace mir |
311 | { |
312 | -namespace test |
313 | +namespace pipe |
314 | { |
315 | |
316 | class Pipe |
317 | { |
318 | public: |
319 | Pipe(); |
320 | + Pipe(int flags); |
321 | ~Pipe(); |
322 | |
323 | int read_fd() const; |
324 | int write_fd() const; |
325 | |
326 | + void close_read_fd(); |
327 | + void close_write_fd(); |
328 | + |
329 | private: |
330 | Pipe(Pipe const&) = delete; |
331 | Pipe& operator=(Pipe const&) = delete; |
332 | |
333 | === modified file 'src/server/CMakeLists.txt' |
334 | --- src/server/CMakeLists.txt 2014-01-22 08:32:55 +0000 |
335 | +++ src/server/CMakeLists.txt 2014-01-28 06:34:09 +0000 |
336 | @@ -12,6 +12,8 @@ |
337 | add_subdirectory(frontend/) |
338 | add_subdirectory(shell/) |
339 | add_subdirectory(time/) |
340 | +add_subdirectory(process/) |
341 | +add_subdirectory(xserver/) |
342 | |
343 | set(PREFIX "${CMAKE_INSTALL_PREFIX}") |
344 | set(EXEC_PREFIX "${CMAKE_INSTALL_PREFIX}") |
345 | @@ -56,6 +58,9 @@ |
346 | mirlttng |
347 | mirnestedgraphics |
348 | miroffscreengraphics |
349 | + mirsharedpipe |
350 | + mirprocess |
351 | + mirxserverintegration |
352 | ) |
353 | |
354 | list(APPEND MIRSERVER_LINKS |
355 | |
356 | === modified file 'src/server/default_server_configuration.cpp' |
357 | --- src/server/default_server_configuration.cpp 2014-01-13 06:12:33 +0000 |
358 | +++ src/server/default_server_configuration.cpp 2014-01-28 06:34:09 +0000 |
359 | @@ -42,6 +42,7 @@ |
360 | #include "mir/time/high_resolution_clock.h" |
361 | #include "mir/geometry/rectangles.h" |
362 | #include "mir/default_configuration.h" |
363 | +#include "mir/xserver/null_server_spawner.h" |
364 | |
365 | #include <map> |
366 | |
367 | @@ -249,3 +250,8 @@ |
368 | return std::make_shared<mir::DefaultServerStatusListener>(); |
369 | }); |
370 | } |
371 | + |
372 | +std::shared_ptr<mir::X::ServerSpawner> mir::DefaultServerConfiguration::the_xserver_spawner() |
373 | +{ |
374 | + return std::make_shared<mir::X::NullServerSpawner>(); |
375 | +} |
376 | |
377 | === added directory 'src/server/process' |
378 | === added file 'src/server/process/CMakeLists.txt' |
379 | --- src/server/process/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
380 | +++ src/server/process/CMakeLists.txt 2014-01-28 06:34:09 +0000 |
381 | @@ -0,0 +1,25 @@ |
382 | +# Copyright © 2014 Canonical Ltd. |
383 | +# |
384 | +# This program is free software: you can redistribute it and/or modify it |
385 | +# under the terms of the GNU General Public License version 3, |
386 | +# as published by the Free Software Foundation. |
387 | +# |
388 | +# This program is distributed in the hope that it will be useful, |
389 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
390 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
391 | +# GNU General Public License for more details. |
392 | +# |
393 | +# You should have received a copy of the GNU General Public License |
394 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
395 | +# |
396 | +# Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> |
397 | + |
398 | +set( |
399 | + PROCESS_SOURCES |
400 | + ${CMAKE_CURRENT_SOURCE_DIR}/fork_spawner.cpp |
401 | +) |
402 | + |
403 | +add_library( |
404 | + mirprocess STATIC |
405 | + ${PROCESS_SOURCES} |
406 | +) |
407 | |
408 | === added file 'src/server/process/fork_spawner.cpp' |
409 | --- src/server/process/fork_spawner.cpp 1970-01-01 00:00:00 +0000 |
410 | +++ src/server/process/fork_spawner.cpp 2014-01-28 06:34:09 +0000 |
411 | @@ -0,0 +1,165 @@ |
412 | +/* |
413 | + * Copyright © 2014 Canonical Ltd. |
414 | + * |
415 | + * This program is free software: you can redistribute it and/or modify it |
416 | + * under the terms of the GNU General Public License version 3, |
417 | + * as published by the Free Software Foundation. |
418 | + * |
419 | + * This program is distributed in the hope that it will be useful, |
420 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
421 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
422 | + * GNU General Public License for more details. |
423 | + * |
424 | + * You should have received a copy of the GNU General Public License |
425 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
426 | + * |
427 | + * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> |
428 | + */ |
429 | + |
430 | +#include "fork_spawner.h" |
431 | + |
432 | +#include "mir/process/handle.h" |
433 | +#include "mir/pipe.h" |
434 | +#include "mir/raii.h" |
435 | + |
436 | +#include <fcntl.h> |
437 | +#include <unistd.h> |
438 | +#include <errno.h> |
439 | +#include <dirent.h> |
440 | +#include <vector> |
441 | +#include <thread> |
442 | +#include <string> |
443 | +#include <set> |
444 | +#include <boost/throw_exception.hpp> |
445 | +#include <boost/exception/errinfo_errno.hpp> |
446 | + |
447 | +namespace |
448 | +{ |
449 | +class PidHandle : public mir::process::Handle |
450 | +{ |
451 | +public: |
452 | + PidHandle(pid_t pid) : child_pid(pid) |
453 | + { |
454 | + } |
455 | + |
456 | + Status status() const override |
457 | + { |
458 | + return Status::Running; |
459 | + } |
460 | + pid_t pid() const override |
461 | + { |
462 | + return child_pid; |
463 | + } |
464 | + |
465 | +private: |
466 | + pid_t child_pid; |
467 | +}; |
468 | + |
469 | +static std::vector<int> open_fds() |
470 | +{ |
471 | + std::vector<int> fds; |
472 | + |
473 | + DIR* process_fds_dir; |
474 | + auto const dir_raii = mir::raii::paired_calls([&process_fds_dir] |
475 | + { process_fds_dir = opendir("/proc/self/fd"); }, |
476 | + [&process_fds_dir] |
477 | + { |
478 | + if (process_fds_dir != nullptr) |
479 | + closedir(process_fds_dir); |
480 | + }); |
481 | + |
482 | + if (process_fds_dir == nullptr) |
483 | + BOOST_THROW_EXCEPTION(boost::enable_error_info(std::runtime_error("Failed to open process fds directory")) |
484 | + << boost::errinfo_errno(errno)); |
485 | + |
486 | + struct dirent* entry; |
487 | + errno = 0; |
488 | + while ((entry = readdir(process_fds_dir)) != nullptr) |
489 | + { |
490 | + // We don't care about directory links |
491 | + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) |
492 | + continue; |
493 | + |
494 | + int fd; |
495 | + char* conversion_end; |
496 | + fd = strtol(entry->d_name, &conversion_end, 10); |
497 | + |
498 | + if (*conversion_end != '\0' || conversion_end == entry->d_name) |
499 | + BOOST_THROW_EXCEPTION(std::runtime_error(std::string("Unexpected fd name: ") + entry->d_name)); |
500 | + |
501 | + if (fd != dirfd(process_fds_dir)) |
502 | + fds.push_back(fd); |
503 | + } |
504 | + if (errno != 0) |
505 | + BOOST_THROW_EXCEPTION(boost::enable_error_info(std::runtime_error("Failed to read from process fds directory")) |
506 | + << boost::errinfo_errno(errno)); |
507 | + |
508 | + return fds; |
509 | +} |
510 | + |
511 | +static std::unique_ptr<mir::process::Handle> run(char const* binary_name, |
512 | + std::initializer_list<char const*> args, |
513 | + std::initializer_list<int> fds) |
514 | +{ |
515 | + mir::pipe::Pipe error_pipe(O_CLOEXEC); |
516 | + pid_t child = fork(); |
517 | + |
518 | + if (child == 0) |
519 | + { |
520 | + std::set<int> precious_fds{fds}; |
521 | + // Don't close stdin, stdout, or stderr |
522 | + precious_fds.insert({0, 1, 2}); |
523 | + // Don't close our cross-process pipe, we need it and know it's CLOEXEC |
524 | + precious_fds.insert(error_pipe.write_fd()); |
525 | + |
526 | + error_pipe.close_read_fd(); |
527 | + |
528 | + for (auto fd : open_fds()) |
529 | + { |
530 | + if (precious_fds.count(fd) == 0) |
531 | + close(fd); |
532 | + } |
533 | + |
534 | + std::vector<char const*> argv; |
535 | + argv.push_back(binary_name); |
536 | + argv.insert(argv.end(), args); |
537 | + argv.push_back(nullptr); |
538 | + |
539 | + execvp(binary_name, const_cast<char* const*>(argv.data())); |
540 | + // We can only get here by failing to exec |
541 | + write(error_pipe.write_fd(), &errno, sizeof errno); |
542 | + } |
543 | + |
544 | + error_pipe.close_write_fd(); |
545 | + error_t error = 0; |
546 | + if (read(error_pipe.read_fd(), &error, sizeof error) == -1) |
547 | + BOOST_THROW_EXCEPTION(boost::enable_error_info(std::runtime_error("Failed to read from pipe")) |
548 | + << boost::errinfo_errno(errno)); |
549 | + |
550 | + if (error != 0) |
551 | + BOOST_THROW_EXCEPTION( |
552 | + boost::enable_error_info(std::runtime_error(std::string("Failed to execute process: ") + binary_name)) |
553 | + << boost::errinfo_errno(errno)); |
554 | + |
555 | + return std::unique_ptr<PidHandle>(new PidHandle(child)); |
556 | +} |
557 | +} |
558 | + |
559 | +std::future<std::unique_ptr<mir::process::Handle>> mir::process::ForkSpawner::run_from_path(char const* binary_name) |
560 | + const |
561 | +{ |
562 | + return std::async( |
563 | + std::launch::async, run, binary_name, std::initializer_list<char const*>(), std::initializer_list<int>()); |
564 | +} |
565 | + |
566 | +std::future<std::unique_ptr<mir::process::Handle>> mir::process::ForkSpawner::run_from_path( |
567 | + char const* binary_name, std::initializer_list<char const*> args) const |
568 | +{ |
569 | + return std::async(std::launch::async, run, binary_name, args, std::initializer_list<int>()); |
570 | +} |
571 | + |
572 | +std::future<std::unique_ptr<mir::process::Handle>> mir::process::ForkSpawner::run_from_path( |
573 | + char const* binary_name, std::initializer_list<char const*> args, std::initializer_list<int> fds) const |
574 | +{ |
575 | + return std::async(std::launch::async, run, binary_name, args, fds); |
576 | +} |
577 | |
578 | === added file 'src/server/process/fork_spawner.h' |
579 | --- src/server/process/fork_spawner.h 1970-01-01 00:00:00 +0000 |
580 | +++ src/server/process/fork_spawner.h 2014-01-28 06:34:09 +0000 |
581 | @@ -0,0 +1,44 @@ |
582 | +/* |
583 | + * Copyright © 2014 Canonical Ltd. |
584 | + * |
585 | + * This program is free software: you can redistribute it and/or modify it |
586 | + * under the terms of the GNU General Public License version 3, |
587 | + * as published by the Free Software Foundation. |
588 | + * |
589 | + * This program is distributed in the hope that it will be useful, |
590 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
591 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
592 | + * GNU General Public License for more details. |
593 | + * |
594 | + * You should have received a copy of the GNU General Public License |
595 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
596 | + * |
597 | + * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> |
598 | + */ |
599 | + |
600 | + |
601 | +#ifndef MIR_PROCESS_FORK_SPAWNER_H_ |
602 | +#define MIR_PROCESS_FORK_SPAWNER_H_ |
603 | + |
604 | +#include "mir/process/spawner.h" |
605 | + |
606 | +namespace mir |
607 | +{ |
608 | +namespace process |
609 | +{ |
610 | + |
611 | +/** |
612 | + * \brief An implementation of mir::process::Spawner that forks new processes |
613 | + */ |
614 | +class ForkSpawner : public Spawner |
615 | +{ |
616 | +public: |
617 | + std::future<std::unique_ptr<Handle>> run_from_path(char const* binary_name) const override; |
618 | + std::future<std::unique_ptr<Handle>> run_from_path(char const* binary_name, std::initializer_list<char const*> args) const override; |
619 | + std::future<std::unique_ptr<Handle>> run_from_path(char const* binary_name, std::initializer_list<char const*> args, std::initializer_list<int> fds) const override; |
620 | +}; |
621 | + |
622 | +} |
623 | +} |
624 | + |
625 | +#endif // MIR_PROCESS_FORK_SPAWNER_H_ |
626 | |
627 | === added directory 'src/server/xserver' |
628 | === added file 'src/server/xserver/CMakeLists.txt' |
629 | --- src/server/xserver/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
630 | +++ src/server/xserver/CMakeLists.txt 2014-01-28 06:34:09 +0000 |
631 | @@ -0,0 +1,12 @@ |
632 | +set( |
633 | + MIR_XSERVER_SRCS |
634 | + |
635 | + global_socket_listening_server_spawner.cpp |
636 | + null_server_spawner.cpp |
637 | +) |
638 | + |
639 | +ADD_LIBRARY( |
640 | + mirxserverintegration STATIC |
641 | + |
642 | + ${MIR_XSERVER_SRCS} |
643 | +) |
644 | |
645 | === added file 'src/server/xserver/global_socket_listening_server_spawner.cpp' |
646 | --- src/server/xserver/global_socket_listening_server_spawner.cpp 1970-01-01 00:00:00 +0000 |
647 | +++ src/server/xserver/global_socket_listening_server_spawner.cpp 2014-01-28 06:34:09 +0000 |
648 | @@ -0,0 +1,71 @@ |
649 | +/* |
650 | + * Copyright © 2014 Canonical Ltd. |
651 | + * |
652 | + * This program is free software: you can redistribute it and/or modify |
653 | + * it under the terms of the GNU General Public License version 3 as |
654 | + * published by the Free Software Foundation. |
655 | + * |
656 | + * This program is distributed in the hope that it will be useful, |
657 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
658 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
659 | + * GNU General Public License for more details. |
660 | + * |
661 | + * You should have received a copy of the GNU General Public License |
662 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
663 | + * |
664 | + * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> |
665 | + */ |
666 | + |
667 | +#include <unistd.h> |
668 | +#include <errno.h> |
669 | + |
670 | +#include <boost/throw_exception.hpp> |
671 | +#include <boost/exception/errinfo_errno.hpp> |
672 | + |
673 | +#include "global_socket_listening_server_spawner.h" |
674 | +#include "mir/process/handle.h" |
675 | + |
676 | +namespace mx = mir::X; |
677 | + |
678 | +mx::GlobalSocketListeningServerContext::GlobalSocketListeningServerContext(std::unique_ptr<mir::process::Handle> server_handle, std::string connection_string) |
679 | + : server_handle(std::move(server_handle)), |
680 | + connection_string(connection_string) |
681 | +{ |
682 | +} |
683 | + |
684 | +char const* mx::GlobalSocketListeningServerContext::client_connection_string() |
685 | +{ |
686 | + return connection_string.c_str(); |
687 | +} |
688 | + |
689 | +std::future<std::unique_ptr<mx::ServerContext>> mx::GlobalSocketListeningServerSpawner::create_server(std::shared_ptr<mir::process::Spawner> const& spawner, std::shared_ptr<mir::frontend::Connector> const& connector) |
690 | +{ |
691 | + return std::async(std::launch::async, [spawner, connector]() |
692 | + { |
693 | + mir::pipe::Pipe displayfd_pipe; |
694 | + auto displayfd = std::to_string(displayfd_pipe.write_fd()); |
695 | + int mir_fd = connector->client_socket_fd(); |
696 | + auto mir_fd_arg = std::string("fd://") + std::to_string(mir_fd); |
697 | + |
698 | + auto future_handle = spawner->run_from_path("Xorg", |
699 | + {"-displayfd", displayfd.c_str(), |
700 | + "-mir", "xserver", |
701 | + "-mirSocket", mir_fd_arg.c_str()}, |
702 | + {displayfd_pipe.write_fd(), mir_fd}); |
703 | + |
704 | + char display_number[10]; |
705 | + errno = 0; |
706 | + int bytes_read = read(displayfd_pipe.read_fd(), display_number, sizeof display_number); |
707 | + |
708 | + while (bytes_read == -1 && errno == EINTR) |
709 | + bytes_read = read(displayfd_pipe.read_fd(), display_number, sizeof display_number);; |
710 | + |
711 | + if (errno != 0) |
712 | + BOOST_THROW_EXCEPTION(boost::enable_error_info(std::runtime_error("Failed to receive display number from Xserver")) |
713 | + << boost::errinfo_errno(errno)); |
714 | + |
715 | + display_number[bytes_read] = '\0'; |
716 | + |
717 | + return std::unique_ptr<mx::ServerContext>(new mx::GlobalSocketListeningServerContext(future_handle.get(), std::string(":") + display_number)); |
718 | + }); |
719 | +} |
720 | |
721 | === added file 'src/server/xserver/global_socket_listening_server_spawner.h' |
722 | --- src/server/xserver/global_socket_listening_server_spawner.h 1970-01-01 00:00:00 +0000 |
723 | +++ src/server/xserver/global_socket_listening_server_spawner.h 2014-01-28 06:34:09 +0000 |
724 | @@ -0,0 +1,52 @@ |
725 | +/* |
726 | + * Copyright © 2014 Canonical Ltd. |
727 | + * |
728 | + * This program is free software: you can redistribute it and/or modify |
729 | + * it under the terms of the GNU General Public License version 3 as |
730 | + * published by the Free Software Foundation. |
731 | + * |
732 | + * This program is distributed in the hope that it will be useful, |
733 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
734 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
735 | + * GNU General Public License for more details. |
736 | + * |
737 | + * You should have received a copy of the GNU General Public License |
738 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
739 | + * |
740 | + * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> |
741 | + */ |
742 | + |
743 | +#ifndef MIR_X_GLOBAL_SOCKET_LISTENING_SERVER_SPAWNER_H_ |
744 | +#define MIR_X_GLOBAL_SOCKET_LISTENING_SERVER_SPAWNER_H_ |
745 | + |
746 | +#include "mir/xserver/xserver_launcher.h" |
747 | +#include "mir/process/spawner.h" |
748 | +#include "mir/pipe.h" |
749 | + |
750 | +#include <memory> |
751 | + |
752 | +namespace mir |
753 | +{ |
754 | +namespace X |
755 | +{ |
756 | +class GlobalSocketListeningServerContext : public ServerContext |
757 | +{ |
758 | +public: |
759 | + GlobalSocketListeningServerContext(std::unique_ptr<mir::process::Handle> server_handle, std::string connection_string); |
760 | + char const* client_connection_string() override; |
761 | + |
762 | +private: |
763 | + std::unique_ptr<mir::process::Handle> server_handle; |
764 | + std::string connection_string; |
765 | +}; |
766 | + |
767 | +class GlobalSocketListeningServerSpawner : public ServerSpawner |
768 | +{ |
769 | +public: |
770 | + std::future<std::unique_ptr<ServerContext>> create_server(std::shared_ptr<mir::process::Spawner> const& spawner, std::shared_ptr<mir::frontend::Connector> const& connector) override; |
771 | +}; |
772 | + |
773 | +} |
774 | +} |
775 | + |
776 | +#endif // MIR_X_GLOBAL_SOCKET_LISTENING_SERVER_SPAWNER_H_ |
777 | |
778 | === added file 'src/server/xserver/null_server_spawner.cpp' |
779 | --- src/server/xserver/null_server_spawner.cpp 1970-01-01 00:00:00 +0000 |
780 | +++ src/server/xserver/null_server_spawner.cpp 2014-01-28 06:34:09 +0000 |
781 | @@ -0,0 +1,34 @@ |
782 | +/* |
783 | + * Copyright © 2014 Canonical Ltd. |
784 | + * |
785 | + * This program is free software: you can redistribute it and/or modify |
786 | + * it under the terms of the GNU General Public License version 3 as |
787 | + * published by the Free Software Foundation. |
788 | + * |
789 | + * This program is distributed in the hope that it will be useful, |
790 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
791 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
792 | + * GNU General Public License for more details. |
793 | + * |
794 | + * You should have received a copy of the GNU General Public License |
795 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
796 | + * |
797 | + * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> |
798 | + */ |
799 | + |
800 | +#include "mir/xserver/null_server_spawner.h" |
801 | + |
802 | +const char* mir::X::NullServerContext::client_connection_string() |
803 | +{ |
804 | + return ""; |
805 | +} |
806 | + |
807 | +std::future<std::unique_ptr<mir::X::ServerContext>> mir::X::NullServerSpawner::create_server( |
808 | + std::shared_ptr<mir::process::Spawner> const& unused, std::shared_ptr<mir::frontend::Connector> const& unuseder) |
809 | +{ |
810 | + static_cast<void>(unused); |
811 | + static_cast<void>(unuseder); |
812 | + std::promise<std::unique_ptr<mir::X::ServerContext>> boring_promise; |
813 | + boring_promise.set_value(std::unique_ptr<mir::X::ServerContext>(new mir::X::NullServerContext)); |
814 | + return boring_promise.get_future(); |
815 | +} |
816 | |
817 | === modified file 'src/shared/CMakeLists.txt' |
818 | --- src/shared/CMakeLists.txt 2014-01-13 06:12:33 +0000 |
819 | +++ src/shared/CMakeLists.txt 2014-01-28 06:34:09 +0000 |
820 | @@ -11,6 +11,7 @@ |
821 | add_subdirectory(logging) |
822 | add_subdirectory(lttng) |
823 | add_subdirectory(env) |
824 | +add_subdirectory(pipe) |
825 | |
826 | set( |
827 | MIR_COMMON_PLATFORM_LIBRARIES |
828 | |
829 | === added directory 'src/shared/pipe' |
830 | === added file 'src/shared/pipe/CMakeLists.txt' |
831 | --- src/shared/pipe/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
832 | +++ src/shared/pipe/CMakeLists.txt 2014-01-28 06:34:09 +0000 |
833 | @@ -0,0 +1,19 @@ |
834 | +# Copyright © 2014 Canonical Ltd. |
835 | +# |
836 | +# This program is free software: you can redistribute it and/or modify |
837 | +# it under the terms of the GNU Lesser General Public License version 3 as |
838 | +# published by the Free Software Foundation. |
839 | +# |
840 | +# This program is distributed in the hope that it will be useful, |
841 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
842 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
843 | +# GNU Lesser General Public License for more details. |
844 | +# |
845 | +# You should have received a copy of the GNU Lesser General Public License |
846 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
847 | +# |
848 | +# Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> |
849 | + |
850 | +add_library(mirsharedpipe STATIC |
851 | + pipe.cpp |
852 | +) |
853 | |
854 | === renamed file 'tests/mir_test/pipe.cpp' => 'src/shared/pipe/pipe.cpp' |
855 | --- tests/mir_test/pipe.cpp 2013-07-02 15:44:55 +0000 |
856 | +++ src/shared/pipe/pipe.cpp 2014-01-28 06:34:09 +0000 |
857 | @@ -2,34 +2,40 @@ |
858 | * Copyright © 2013 Canonical Ltd. |
859 | * |
860 | * This program is free software: you can redistribute it and/or modify it |
861 | - * under the terms of the GNU General Public License version 3, |
862 | + * under the terms of the GNU Lesser General Public License version 3, |
863 | * as published by the Free Software Foundation. |
864 | * |
865 | * This program is distributed in the hope that it will be useful, |
866 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
867 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
868 | - * GNU General Public License for more details. |
869 | + * GNU Lesser General Public License for more details. |
870 | * |
871 | - * You should have received a copy of the GNU General Public License |
872 | + * You should have received a copy of the GNU Lesser General Public License |
873 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
874 | * |
875 | * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com> |
876 | */ |
877 | |
878 | -#include <mir_test/pipe.h> |
879 | +#include "mir/pipe.h" |
880 | |
881 | #include <boost/throw_exception.hpp> |
882 | #include <boost/exception/errinfo_errno.hpp> |
883 | |
884 | #include <stdexcept> |
885 | |
886 | +#include <fcntl.h> |
887 | #include <unistd.h> |
888 | |
889 | -namespace mt = mir::test; |
890 | - |
891 | -mt::Pipe::Pipe() |
892 | -{ |
893 | - if (pipe(pipefd)) |
894 | +namespace mp = mir::pipe; |
895 | + |
896 | +mp::Pipe::Pipe() |
897 | + : Pipe(0) |
898 | +{ |
899 | +} |
900 | + |
901 | +mp::Pipe::Pipe(int flags) |
902 | +{ |
903 | + if (::pipe2(pipefd, flags)) |
904 | { |
905 | BOOST_THROW_EXCEPTION( |
906 | boost::enable_error_info(std::runtime_error("Failed to create pipe")) |
907 | @@ -37,18 +43,32 @@ |
908 | } |
909 | } |
910 | |
911 | -mt::Pipe::~Pipe() |
912 | +mp::Pipe::~Pipe() |
913 | +{ |
914 | + if (pipefd[0] != -1) |
915 | + close(pipefd[0]); |
916 | + if (pipefd[1] != -1) |
917 | + close(pipefd[1]); |
918 | +} |
919 | + |
920 | +int mp::Pipe::read_fd() const |
921 | +{ |
922 | + return pipefd[0]; |
923 | +} |
924 | + |
925 | +int mp::Pipe::write_fd() const |
926 | +{ |
927 | + return pipefd[1]; |
928 | +} |
929 | + |
930 | +void mp::Pipe::close_read_fd() |
931 | { |
932 | close(pipefd[0]); |
933 | + pipefd[0] = -1; |
934 | +} |
935 | + |
936 | +void mp::Pipe::close_write_fd() |
937 | +{ |
938 | close(pipefd[1]); |
939 | -} |
940 | - |
941 | -int mt::Pipe::read_fd() const |
942 | -{ |
943 | - return pipefd[0]; |
944 | -} |
945 | - |
946 | -int mt::Pipe::write_fd() const |
947 | -{ |
948 | - return pipefd[1]; |
949 | + pipefd[1] = -1; |
950 | } |
951 | |
952 | === modified file 'tests/CMakeLists.txt' |
953 | --- tests/CMakeLists.txt 2014-01-23 08:06:05 +0000 |
954 | +++ tests/CMakeLists.txt 2014-01-28 06:34:09 +0000 |
955 | @@ -36,6 +36,7 @@ |
956 | |
957 | if (MIR_BUILD_ACCEPTANCE_TESTS) |
958 | add_subdirectory(acceptance-tests/) |
959 | + pkg_check_modules(X11 REQUIRED x11) |
960 | endif (MIR_BUILD_ACCEPTANCE_TESTS) |
961 | |
962 | if (MIR_BUILD_INTEGRATION_TESTS) |
963 | |
964 | === modified file 'tests/acceptance-tests/CMakeLists.txt' |
965 | --- tests/acceptance-tests/CMakeLists.txt 2014-01-24 11:15:31 +0000 |
966 | +++ tests/acceptance-tests/CMakeLists.txt 2014-01-28 06:34:09 +0000 |
967 | @@ -27,6 +27,8 @@ |
968 | test_server_disconnect.cpp |
969 | test_client_library_drm.cpp |
970 | test_protobuf.cpp |
971 | + test_subprocess.cpp |
972 | + test_xserver_spawner.cpp |
973 | ${GENERATED_PROTOBUF_SRCS} |
974 | ${GENERATED_PROTOBUF_HDRS} |
975 | ) |
976 | @@ -65,6 +67,7 @@ |
977 | ${GMOCK_LIBRARY} |
978 | ${GMOCK_MAIN_LIBRARY} |
979 | ${CMAKE_THREAD_LIBS_INIT} # Link in pthread. |
980 | + ${X11_LDFLAGS} |
981 | ) |
982 | |
983 | CMAKE_DEPENDENT_OPTION( |
984 | |
985 | === modified file 'tests/acceptance-tests/test_display_configuration.cpp' |
986 | --- tests/acceptance-tests/test_display_configuration.cpp 2014-01-13 06:12:33 +0000 |
987 | +++ tests/acceptance-tests/test_display_configuration.cpp 2014-01-28 06:34:09 +0000 |
988 | @@ -20,6 +20,7 @@ |
989 | #include "mir/frontend/session_authorizer.h" |
990 | #include "mir/graphics/event_handler_register.h" |
991 | #include "src/server/scene/global_event_sender.h" |
992 | +#include "mir/pipe.h" |
993 | |
994 | #include "mir_test_framework/display_server_test_fixture.h" |
995 | #include "mir_test_framework/cross_process_sync.h" |
996 | @@ -32,7 +33,6 @@ |
997 | #include "mir_test_doubles/stub_display_configuration.h" |
998 | #include "mir_test_doubles/stub_buffer_allocator.h" |
999 | #include "mir_test/fake_shared.h" |
1000 | -#include "mir_test/pipe.h" |
1001 | #include "mir_test/cross_process_action.h" |
1002 | |
1003 | #include "mir_toolkit/mir_client_library.h" |
1004 | @@ -49,6 +49,7 @@ |
1005 | namespace mtf = mir_test_framework; |
1006 | namespace mtd = mir::test::doubles; |
1007 | namespace mt = mir::test; |
1008 | +namespace mp = mir::pipe; |
1009 | |
1010 | namespace |
1011 | { |
1012 | @@ -127,7 +128,7 @@ |
1013 | private: |
1014 | std::shared_ptr<mg::DisplayConfiguration> config; |
1015 | mtd::NullDisplayBuffer display_buffer; |
1016 | - mt::Pipe p; |
1017 | + mp::Pipe p; |
1018 | std::atomic<bool> handler_called; |
1019 | }; |
1020 | |
1021 | |
1022 | === added file 'tests/acceptance-tests/test_subprocess.cpp' |
1023 | --- tests/acceptance-tests/test_subprocess.cpp 1970-01-01 00:00:00 +0000 |
1024 | +++ tests/acceptance-tests/test_subprocess.cpp 2014-01-28 06:34:09 +0000 |
1025 | @@ -0,0 +1,166 @@ |
1026 | +/* |
1027 | + * Copyright © 2014 Canonical Ltd. |
1028 | + * |
1029 | + * This program is free software: you can redistribute it and/or modify it |
1030 | + * under the terms of the GNU General Public License version 3, |
1031 | + * as published by the Free Software Foundation. |
1032 | + * |
1033 | + * This program is distributed in the hope that it will be useful, |
1034 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1035 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1036 | + * GNU General Public License for more details. |
1037 | + * |
1038 | + * You should have received a copy of the GNU General Public License |
1039 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1040 | + * |
1041 | + * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> |
1042 | + */ |
1043 | + |
1044 | +#include "src/server/process/fork_spawner.h" |
1045 | +#include "mir/process/spawner.h" |
1046 | +#include "mir/process/handle.h" |
1047 | + |
1048 | +#include <fstream> |
1049 | +#include <sstream> |
1050 | +#include <unistd.h> |
1051 | +#include <thread> |
1052 | +#include <sys/types.h> |
1053 | +#include <sys/stat.h> |
1054 | +#include <fcntl.h> |
1055 | +#include <dirent.h> |
1056 | +#include <errno.h> |
1057 | +#include <string.h> |
1058 | + |
1059 | +#include <gtest/gtest.h> |
1060 | +#include <gmock/gmock.h> |
1061 | + |
1062 | +TEST(ProcessTest, RunFromPathRunsCorrectBinary) |
1063 | +{ |
1064 | + mir::process::ForkSpawner spawner; |
1065 | + |
1066 | + auto future_handle = spawner.run_from_path("true"); |
1067 | + auto handle = future_handle.get(); |
1068 | + |
1069 | + std::stringstream stat_path; |
1070 | + stat_path << "/proc/" << handle->pid() << "/stat"; |
1071 | + |
1072 | + std::ifstream status{stat_path.str()}; |
1073 | + |
1074 | + char binary_name[PATH_MAX]; |
1075 | + |
1076 | + // Read up to the initial '(' |
1077 | + while (status.good() && !status.eof() && (status.get() != '(')) |
1078 | + ; |
1079 | + |
1080 | + status.getline(binary_name, sizeof(binary_name), ')'); |
1081 | + |
1082 | + EXPECT_STREQ("true", binary_name); |
1083 | +} |
1084 | + |
1085 | +TEST(ProcessTest, ChildHasExpectedFDs) |
1086 | +{ |
1087 | + mir::process::ForkSpawner spawner; |
1088 | + |
1089 | + auto fd = open("/dev/null", O_RDONLY); |
1090 | + auto future_handle = spawner.run_from_path("sleep", {"1"}); |
1091 | + auto handle = future_handle.get(); |
1092 | + |
1093 | + // TODO: Why is this racy? |
1094 | + std::this_thread::sleep_for(std::chrono::milliseconds{1}); |
1095 | + |
1096 | + std::stringstream fds_path; |
1097 | + fds_path << "/proc/" << handle->pid() << "/fd"; |
1098 | + |
1099 | + bool found_stdin = false, found_stdout = false, found_stderr = false; |
1100 | + |
1101 | + DIR* process_fds_dir = opendir(fds_path.str().c_str()); |
1102 | + ASSERT_NE(process_fds_dir, nullptr) << "Error opening " << fds_path.str() << ": " << strerror(errno) << " (" |
1103 | + << errno << ")" << std::endl; |
1104 | + struct dirent* entry; |
1105 | + while ((entry = readdir(process_fds_dir)) != nullptr) |
1106 | + { |
1107 | + // We don't care about directory links |
1108 | + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) |
1109 | + continue; |
1110 | + |
1111 | + if (strcmp(entry->d_name, "0") == 0) |
1112 | + { |
1113 | + found_stdin = true; |
1114 | + } |
1115 | + else if (strcmp(entry->d_name, "1") == 0) |
1116 | + { |
1117 | + found_stdout = true; |
1118 | + } |
1119 | + else if (strcmp(entry->d_name, "2") == 0) |
1120 | + { |
1121 | + found_stderr = true; |
1122 | + } |
1123 | + else |
1124 | + { |
1125 | + ADD_FAILURE() << "Unexpected fd: " << entry->d_name << std::endl; |
1126 | + } |
1127 | + } |
1128 | + EXPECT_TRUE(found_stdin); |
1129 | + EXPECT_TRUE(found_stdout); |
1130 | + EXPECT_TRUE(found_stderr); |
1131 | + closedir(process_fds_dir); |
1132 | + close(fd); |
1133 | +} |
1134 | + |
1135 | +TEST(ProcessTest, ChildReceivesExpectedCmdline) |
1136 | +{ |
1137 | + mir::process::ForkSpawner spawner; |
1138 | + |
1139 | + auto future_handle = spawner.run_from_path("sleep", {"10"}); |
1140 | + auto handle = future_handle.get(); |
1141 | + |
1142 | + std::stringstream cmdline_path; |
1143 | + cmdline_path << "/proc/" << handle->pid() << "/cmdline"; |
1144 | + |
1145 | + std::ifstream cmdline{cmdline_path.str()}; |
1146 | + |
1147 | + // We expect "sleep\010\0\0" |
1148 | + char buffer[5 + 1 + 2 + 1 + 1]; |
1149 | + cmdline.read(buffer, sizeof(buffer)); |
1150 | + |
1151 | + EXPECT_STREQ("sleep", buffer); |
1152 | + EXPECT_STREQ("10", buffer + 6); |
1153 | +} |
1154 | + |
1155 | +TEST(ProcessTest, SpawningNonExistentBinaryThrows) |
1156 | +{ |
1157 | + mir::process::ForkSpawner spawner; |
1158 | + |
1159 | + auto future_handle = spawner.run_from_path("I'm a binary that almost certainly doesn't exist"); |
1160 | + |
1161 | + EXPECT_THROW(future_handle.get(), std::runtime_error); |
1162 | +} |
1163 | + |
1164 | +TEST(ProcessTest, ChildRetainsSetFDs) |
1165 | +{ |
1166 | + mir::process::ForkSpawner spawner; |
1167 | + |
1168 | + auto fd = open("/dev/null", 0); |
1169 | + auto future_handle = spawner.run_from_path("sleep", {"1"}, {fd}); |
1170 | + auto handle = future_handle.get(); |
1171 | + |
1172 | + // TODO: Why is this racy? |
1173 | + |
1174 | + std::stringstream fds_path; |
1175 | + fds_path << "/proc/" << handle->pid() << "/fd"; |
1176 | + |
1177 | + bool found_our_fd = false; |
1178 | + |
1179 | + DIR* process_fds_dir = opendir(fds_path.str().c_str()); |
1180 | + ASSERT_NE(process_fds_dir, nullptr) << "Error opening " << fds_path.str() << ": " << strerror(errno) << " (" |
1181 | + << errno << ")" << std::endl; |
1182 | + struct dirent* entry; |
1183 | + while ((entry = readdir(process_fds_dir)) != nullptr) |
1184 | + { |
1185 | + if (atoi(entry->d_name) == fd) |
1186 | + found_our_fd = true; |
1187 | + } |
1188 | + EXPECT_TRUE(found_our_fd); |
1189 | + closedir(process_fds_dir); |
1190 | + close(fd); |
1191 | +} |
1192 | |
1193 | === added file 'tests/acceptance-tests/test_xserver_spawner.cpp' |
1194 | --- tests/acceptance-tests/test_xserver_spawner.cpp 1970-01-01 00:00:00 +0000 |
1195 | +++ tests/acceptance-tests/test_xserver_spawner.cpp 2014-01-28 06:34:09 +0000 |
1196 | @@ -0,0 +1,73 @@ |
1197 | +/* |
1198 | + * Copyright © 2014 Canonical Ltd. |
1199 | + * |
1200 | + * This program is free software: you can redistribute it and/or modify |
1201 | + * it under the terms of the GNU General Public License version 3 as |
1202 | + * published by the Free Software Foundation. |
1203 | + * |
1204 | + * This program is distributed in the hope that it will be useful, |
1205 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1206 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1207 | + * GNU General Public License for more details. |
1208 | + * |
1209 | + * You should have received a copy of the GNU General Public License |
1210 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1211 | + * |
1212 | + * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> |
1213 | + */ |
1214 | + |
1215 | +#include "src/server/xserver/global_socket_listening_server_spawner.h" |
1216 | +#include "mir_test_framework/testing_server_configuration.h" |
1217 | +#include "mir_test_framework/in_process_server.h" |
1218 | +#include "mir/xserver/xserver_launcher.h" |
1219 | +#include "src/server/process/fork_spawner.h" |
1220 | + |
1221 | +#include <X11/Xlib.h> |
1222 | +#include <stdlib.h> |
1223 | + |
1224 | +#include <gtest/gtest.h> |
1225 | + |
1226 | +namespace mtf = mir_test_framework; |
1227 | +namespace mx = mir::X; |
1228 | + |
1229 | +namespace |
1230 | +{ |
1231 | +struct XserverSpawningServer : public mtf::InProcessServer |
1232 | +{ |
1233 | +public: |
1234 | + std::shared_ptr<mx::ServerSpawner> the_xserver_spawner() |
1235 | + { |
1236 | + return config.the_xserver_spawner(); |
1237 | + } |
1238 | + |
1239 | + mir::DefaultServerConfiguration& server_config() override |
1240 | + { |
1241 | + return config; |
1242 | + } |
1243 | + |
1244 | + class SocketListeningXServerConfig : public mtf::StubbedServerConfiguration |
1245 | + { |
1246 | + public: |
1247 | + std::shared_ptr<mx::ServerSpawner> the_xserver_spawner() override |
1248 | + { |
1249 | + return std::make_shared<mx::GlobalSocketListeningServerSpawner> (); |
1250 | + } |
1251 | + } config; |
1252 | +}; |
1253 | +} |
1254 | + |
1255 | +// This requires a bit more fiddling before it will work. |
1256 | +// Particularly, it needs an xorg.conf that specifies dummy |
1257 | +// devices, so the real devices don't fail to load. |
1258 | +TEST_F(XserverSpawningServer, DISABLED_X11ClientConnects) |
1259 | +{ |
1260 | + // Ensure the surrounding environment doesn't mess with the test |
1261 | + unsetenv("DISPLAY"); |
1262 | + |
1263 | + auto xserver = the_xserver_spawner()->create_server(std::make_shared<mir::process::ForkSpawner>(), config.the_connector()); |
1264 | + Display* disp = XOpenDisplay(xserver.get()->client_connection_string()); |
1265 | + |
1266 | + ASSERT_TRUE(disp != NULL); |
1267 | + |
1268 | + XCloseDisplay(disp); |
1269 | +} |
1270 | |
1271 | === modified file 'tests/integration-tests/test_display_server_main_loop_events.cpp' |
1272 | --- tests/integration-tests/test_display_server_main_loop_events.cpp 2014-01-13 06:12:33 +0000 |
1273 | +++ tests/integration-tests/test_display_server_main_loop_events.cpp 2014-01-28 06:34:09 +0000 |
1274 | @@ -23,8 +23,8 @@ |
1275 | #include "mir/main_loop.h" |
1276 | #include "mir/display_changer.h" |
1277 | #include "mir/server_status_listener.h" |
1278 | +#include "mir/pipe.h" |
1279 | |
1280 | -#include "mir_test/pipe.h" |
1281 | #include "mir_test_framework/testing_server_configuration.h" |
1282 | #include "mir_test_doubles/mock_input_manager.h" |
1283 | #include "mir_test_doubles/mock_compositor.h" |
1284 | @@ -46,7 +46,7 @@ |
1285 | namespace mc = mir::compositor; |
1286 | namespace mg = mir::graphics; |
1287 | namespace mf = mir::frontend; |
1288 | -namespace mt = mir::test; |
1289 | +namespace mp = mir::pipe; |
1290 | namespace mtd = mir::test::doubles; |
1291 | namespace mtf = mir_test_framework; |
1292 | |
1293 | @@ -314,7 +314,7 @@ |
1294 | std::shared_ptr<mtd::MockInputManager> mock_input_manager; |
1295 | std::shared_ptr<MockDisplayChanger> mock_display_changer; |
1296 | |
1297 | - mt::Pipe p; |
1298 | + mp::Pipe p; |
1299 | int const pause_signal; |
1300 | int const resume_signal; |
1301 | }; |
1302 | |
1303 | === modified file 'tests/mir_test/CMakeLists.txt' |
1304 | --- tests/mir_test/CMakeLists.txt 2013-08-28 03:41:48 +0000 |
1305 | +++ tests/mir_test/CMakeLists.txt 2014-01-28 06:34:09 +0000 |
1306 | @@ -1,7 +1,6 @@ |
1307 | add_library( |
1308 | mir-test STATIC |
1309 | |
1310 | - pipe.cpp |
1311 | display_config_matchers.cpp |
1312 | cross_process_action.cpp |
1313 | ) |
1314 | |
1315 | === modified file 'tests/unit-tests/CMakeLists.txt' |
1316 | --- tests/unit-tests/CMakeLists.txt 2014-01-23 08:21:21 +0000 |
1317 | +++ tests/unit-tests/CMakeLists.txt 2014-01-28 06:34:09 +0000 |
1318 | @@ -11,6 +11,7 @@ |
1319 | test_asio_main_loop.cpp |
1320 | shared_library_test.cpp |
1321 | test_raii.cpp |
1322 | + test_fork_spawner.cpp |
1323 | ) |
1324 | |
1325 | if (UMOCKDEV_REQUIRED) |
1326 | @@ -29,6 +30,7 @@ |
1327 | add_subdirectory(android_input/) |
1328 | add_subdirectory(scene/) |
1329 | add_subdirectory(draw/) |
1330 | +add_subdirectory(xserver/) |
1331 | |
1332 | add_executable(mir_unit_tests ${UNIT_TEST_SOURCES}) |
1333 | uses_android_input(mir_unit_tests) |
1334 | @@ -42,6 +44,7 @@ |
1335 | mirdraw |
1336 | mirtestdraw |
1337 | mirlogging |
1338 | + mirxserverintegration |
1339 | |
1340 | mir-test |
1341 | mir-test-doubles |
1342 | |
1343 | === modified file 'tests/unit-tests/test_asio_main_loop.cpp' |
1344 | --- tests/unit-tests/test_asio_main_loop.cpp 2014-01-13 06:12:33 +0000 |
1345 | +++ tests/unit-tests/test_asio_main_loop.cpp 2014-01-28 06:34:09 +0000 |
1346 | @@ -17,7 +17,7 @@ |
1347 | */ |
1348 | |
1349 | #include "mir/asio_main_loop.h" |
1350 | -#include "mir_test/pipe.h" |
1351 | +#include "mir/pipe.h" |
1352 | |
1353 | #include <gtest/gtest.h> |
1354 | |
1355 | @@ -27,7 +27,7 @@ |
1356 | #include <sys/types.h> |
1357 | #include <unistd.h> |
1358 | |
1359 | -namespace mt = mir::test; |
1360 | +namespace mp = mir::pipe; |
1361 | |
1362 | TEST(AsioMainLoopTest, signal_handled) |
1363 | { |
1364 | @@ -148,7 +148,7 @@ |
1365 | |
1366 | TEST(AsioMainLoopTest, fd_data_handled) |
1367 | { |
1368 | - mt::Pipe p; |
1369 | + mp::Pipe p; |
1370 | char const data_to_write{'a'}; |
1371 | int handled_fd{0}; |
1372 | char data_read{0}; |
1373 | @@ -173,7 +173,7 @@ |
1374 | |
1375 | TEST(AsioMainLoopTest, multiple_fds_with_single_handler_handled) |
1376 | { |
1377 | - std::vector<mt::Pipe> const pipes(2); |
1378 | + std::vector<mp::Pipe> const pipes(2); |
1379 | size_t const num_elems_to_send{10}; |
1380 | std::vector<int> handled_fds; |
1381 | std::vector<size_t> elems_read; |
1382 | @@ -223,7 +223,7 @@ |
1383 | |
1384 | TEST(AsioMainLoopTest, multiple_fd_handlers_are_called) |
1385 | { |
1386 | - std::vector<mt::Pipe> const pipes(3); |
1387 | + std::vector<mp::Pipe> const pipes(3); |
1388 | std::vector<int> const elems_to_send{10,11,12}; |
1389 | std::vector<int> handled_fds{0,0,0}; |
1390 | std::vector<int> elems_read{0,0,0}; |
1391 | |
1392 | === added file 'tests/unit-tests/test_fork_spawner.cpp' |
1393 | --- tests/unit-tests/test_fork_spawner.cpp 1970-01-01 00:00:00 +0000 |
1394 | +++ tests/unit-tests/test_fork_spawner.cpp 2014-01-28 06:34:09 +0000 |
1395 | @@ -0,0 +1,64 @@ |
1396 | +/* |
1397 | + * Copyright © 2014 Canonical Ltd. |
1398 | + * |
1399 | + * This program is free software: you can redistribute it and/or modify it |
1400 | + * under the terms of the GNU General Public License version 3, |
1401 | + * as published by the Free Software Foundation. |
1402 | + * |
1403 | + * This program is distributed in the hope that it will be useful, |
1404 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1405 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1406 | + * GNU General Public License for more details. |
1407 | + * |
1408 | + * You should have received a copy of the GNU General Public License |
1409 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1410 | + * |
1411 | + * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> |
1412 | + */ |
1413 | + |
1414 | +#include "src/server/process/fork_spawner.cpp" |
1415 | + |
1416 | +#include <set> |
1417 | + |
1418 | +#include <gtest/gtest.h> |
1419 | +#include <gmock/gmock.h> |
1420 | + |
1421 | +using namespace testing; |
1422 | + |
1423 | +TEST(ForkSpawnerTest, OpenFDsListsCorrectFDs) |
1424 | +{ |
1425 | + // We start with stdin, stdout stderr |
1426 | + std::set<int> std_fds{0, 1, 2}; |
1427 | + std::set<int> extra_fds; |
1428 | + |
1429 | + extra_fds.insert(open("/dev/null", O_RDONLY)); |
1430 | + |
1431 | + // Ensure we have a hole in our fd numbering |
1432 | + int spacing_fd = open("/dev/null", O_RDONLY); |
1433 | + |
1434 | + extra_fds.insert(open("/dev/null", O_RDONLY)); |
1435 | + extra_fds.insert(open("/dev/null", O_RDONLY)); |
1436 | + |
1437 | + close(spacing_fd); |
1438 | + |
1439 | + for (auto fd : open_fds()) |
1440 | + { |
1441 | + if (std_fds.erase(fd) != 1) |
1442 | + { |
1443 | + if (extra_fds.erase(fd) != 1) |
1444 | + { |
1445 | + // We can't fail on unexpected fd as we don't control our test environment |
1446 | + // sufficiently - ctest leaves fds open. |
1447 | + close(fd); |
1448 | + } |
1449 | + } |
1450 | + } |
1451 | + |
1452 | + EXPECT_THAT(std_fds, IsEmpty()); |
1453 | + EXPECT_THAT(extra_fds, IsEmpty()); |
1454 | + |
1455 | + for (auto fd : extra_fds) |
1456 | + { |
1457 | + close(fd); |
1458 | + } |
1459 | +} |
1460 | |
1461 | === added directory 'tests/unit-tests/xserver' |
1462 | === added file 'tests/unit-tests/xserver/CMakeLists.txt' |
1463 | --- tests/unit-tests/xserver/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
1464 | +++ tests/unit-tests/xserver/CMakeLists.txt 2014-01-28 06:34:09 +0000 |
1465 | @@ -0,0 +1,8 @@ |
1466 | +list(APPEND UNIT_TEST_SOURCES |
1467 | + ${CMAKE_CURRENT_SOURCE_DIR}/test_xserver_spawner.cpp |
1468 | +) |
1469 | + |
1470 | +set( |
1471 | + UNIT_TEST_SOURCES |
1472 | + ${UNIT_TEST_SOURCES} |
1473 | + PARENT_SCOPE) |
1474 | |
1475 | === added file 'tests/unit-tests/xserver/test_xserver_spawner.cpp' |
1476 | --- tests/unit-tests/xserver/test_xserver_spawner.cpp 1970-01-01 00:00:00 +0000 |
1477 | +++ tests/unit-tests/xserver/test_xserver_spawner.cpp 2014-01-28 06:34:09 +0000 |
1478 | @@ -0,0 +1,206 @@ |
1479 | +/* |
1480 | + * Copyright © 2014 Canonical Ltd. |
1481 | + * |
1482 | + * This program is free software: you can redistribute it and/or modify |
1483 | + * it under the terms of the GNU General Public License version 3 as |
1484 | + * published by the Free Software Foundation. |
1485 | + * |
1486 | + * This program is distributed in the hope that it will be useful, |
1487 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1488 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1489 | + * GNU General Public License for more details. |
1490 | + * |
1491 | + * You should have received a copy of the GNU General Public License |
1492 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1493 | + * |
1494 | + * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com> |
1495 | + */ |
1496 | + |
1497 | +#include <future> |
1498 | +#include <vector> |
1499 | +#include <thread> |
1500 | +#include <condition_variable> |
1501 | + |
1502 | +#include <gtest/gtest.h> |
1503 | +#include <gmock/gmock.h> |
1504 | + |
1505 | +#include "mir/process/spawner.h" |
1506 | +#include "mir/process/handle.h" |
1507 | + |
1508 | +#include "src/server/xserver/global_socket_listening_server_spawner.h" |
1509 | + |
1510 | +using namespace ::testing; |
1511 | + |
1512 | +namespace |
1513 | +{ |
1514 | +struct MockConnector : public mir::frontend::Connector |
1515 | +{ |
1516 | + MOCK_METHOD0(start, void(void)); |
1517 | + MOCK_METHOD0(stop, void(void)); |
1518 | + MOCK_CONST_METHOD0(client_socket_fd, int(void)); |
1519 | + MOCK_CONST_METHOD0(remove_endpoint, void(void)); |
1520 | +}; |
1521 | + |
1522 | +struct MockProcessSpawner : public mir::process::Spawner |
1523 | +{ |
1524 | + MOCK_CONST_METHOD3(run, mir::process::Handle*(std::string, std::vector<char const*>, std::vector<int>)); |
1525 | + |
1526 | + std::future<std::unique_ptr<mir::process::Handle>> run_from_path(char const* binary) const override |
1527 | + { |
1528 | + std::promise<std::unique_ptr<mir::process::Handle>> dummy_promise; |
1529 | + dummy_promise.set_value(std::unique_ptr<mir::process::Handle>( |
1530 | + run(binary, std::initializer_list<char const*>(), std::initializer_list<int>()))); |
1531 | + return dummy_promise.get_future(); |
1532 | + } |
1533 | + std::future<std::unique_ptr<mir::process::Handle>> run_from_path(char const* binary, |
1534 | + std::initializer_list<char const*> args) const |
1535 | + override |
1536 | + { |
1537 | + std::promise<std::unique_ptr<mir::process::Handle>> dummy_promise; |
1538 | + dummy_promise.set_value(std::unique_ptr<mir::process::Handle>(run(binary, args, std::initializer_list<int>()))); |
1539 | + return dummy_promise.get_future(); |
1540 | + } |
1541 | + |
1542 | + std::future<std::unique_ptr<mir::process::Handle>> run_from_path(char const* binary, |
1543 | + std::initializer_list<char const*> args, |
1544 | + std::initializer_list<int> fds) const override |
1545 | + { |
1546 | + std::promise<std::unique_ptr<mir::process::Handle>> dummy_promise; |
1547 | + dummy_promise.set_value(std::unique_ptr<mir::process::Handle>(run(binary, args, fds))); |
1548 | + return dummy_promise.get_future(); |
1549 | + } |
1550 | +}; |
1551 | + |
1552 | +struct SocketListeningServerTest : public testing::Test |
1553 | +{ |
1554 | + SocketListeningServerTest() |
1555 | + : default_server_number("100"), |
1556 | + spawner(std::make_shared<NiceMock<MockProcessSpawner>>()), |
1557 | + connector(std::make_shared<NiceMock<MockConnector>>()) |
1558 | + { |
1559 | + ON_CALL(*spawner, run(_, _, _)) |
1560 | + .WillByDefault(DoAll(SaveArg<0>(&binary), |
1561 | + SaveArg<1>(&args), |
1562 | + SaveArg<2>(&fds), |
1563 | + InvokeWithoutArgs([this]() |
1564 | + { write_server_string(default_server_number); }), |
1565 | + Return(nullptr))); |
1566 | + ON_CALL(*connector, client_socket_fd()) |
1567 | + .WillByDefault(Return(22)); |
1568 | + } |
1569 | + |
1570 | + void write_server_string(std::string server_number) |
1571 | + { |
1572 | + auto location = std::find_if(args.begin(), args.end(), [](char const* a) |
1573 | + { return strcmp(a, "-displayfd") == 0; }); |
1574 | + ASSERT_NE(location, args.end()); |
1575 | + ASSERT_NE(++location, args.end()); |
1576 | + int server_fd = atoi(*location); |
1577 | + write(server_fd, server_number.data(), server_number.length()); |
1578 | + close(server_fd); |
1579 | + } |
1580 | + |
1581 | + std::string default_server_number; |
1582 | + std::string binary; |
1583 | + std::vector<char const*> args; |
1584 | + std::vector<int> fds; |
1585 | + std::shared_ptr<NiceMock<MockProcessSpawner>> spawner; |
1586 | + std::shared_ptr<NiceMock<MockConnector>> connector; |
1587 | +}; |
1588 | +} |
1589 | + |
1590 | +TEST_F(SocketListeningServerTest, CreateServerAlwaysValid) |
1591 | +{ |
1592 | + mir::X::GlobalSocketListeningServerSpawner factory; |
1593 | + |
1594 | + auto server_context = factory.create_server(spawner, connector); |
1595 | + ASSERT_NE(server_context.get(), nullptr); |
1596 | +} |
1597 | + |
1598 | +TEST_F(SocketListeningServerTest, SpawnsCorrectExecutable) |
1599 | +{ |
1600 | + mir::X::GlobalSocketListeningServerSpawner factory; |
1601 | + |
1602 | + auto server_context = factory.create_server(spawner, connector); |
1603 | + server_context.get(); |
1604 | + |
1605 | + EXPECT_EQ(binary, "Xorg"); |
1606 | +} |
1607 | + |
1608 | +namespace |
1609 | +{ |
1610 | +MATCHER_P(ContainsSubsequence, subsequence, "") |
1611 | +{ |
1612 | + auto location = |
1613 | + std::search(arg.begin(), arg.end(), subsequence.begin(), subsequence.end(), [](char const* a, std::string b) |
1614 | + { return strcmp(a, b.c_str()) == 0; }); |
1615 | + return location != arg.end(); |
1616 | +} |
1617 | +} |
1618 | + |
1619 | +TEST_F(SocketListeningServerTest, SpawnsWithDisplayFDSet) |
1620 | +{ |
1621 | + mir::X::GlobalSocketListeningServerSpawner factory; |
1622 | + |
1623 | + auto server_context = factory.create_server(spawner, connector); |
1624 | + server_context.get(); |
1625 | + |
1626 | + ASSERT_THAT(args, Not(IsEmpty())); |
1627 | + ASSERT_THAT(fds, Not(IsEmpty())); |
1628 | + |
1629 | + Matcher<std::vector<char const*>> fd_matcher = Not(_); |
1630 | + for (auto fd : fds) |
1631 | + { |
1632 | + fd_matcher = AnyOf(fd_matcher, ContainsSubsequence(std::vector<std::string>{"-displayfd", std::to_string(fd)})); |
1633 | + } |
1634 | + EXPECT_THAT(args, fd_matcher); |
1635 | +} |
1636 | + |
1637 | +TEST_F(SocketListeningServerTest, ReturnsCorrectDisplayString) |
1638 | +{ |
1639 | + mir::X::GlobalSocketListeningServerSpawner factory; |
1640 | + |
1641 | + default_server_number = "20"; |
1642 | + auto server_context = factory.create_server(spawner, connector); |
1643 | + |
1644 | + EXPECT_STREQ(":20", server_context.get()->client_connection_string()); |
1645 | +} |
1646 | + |
1647 | +TEST_F(SocketListeningServerTest, HandlesSpawnerLifecycleCorrectly) |
1648 | +{ |
1649 | + mir::X::GlobalSocketListeningServerSpawner factory; |
1650 | + |
1651 | + std::future<std::unique_ptr<mir::X::ServerContext>> server_context; |
1652 | + |
1653 | + { |
1654 | + auto tmp_spawner = std::make_shared<MockProcessSpawner>(); |
1655 | + ON_CALL(*tmp_spawner, run(_, _, _)) |
1656 | + .WillByDefault(DoAll(SaveArg<0>(&binary), |
1657 | + SaveArg<1>(&args), |
1658 | + SaveArg<2>(&fds), |
1659 | + InvokeWithoutArgs([this]() |
1660 | + { write_server_string(default_server_number); }), |
1661 | + Return(nullptr))); |
1662 | + EXPECT_CALL(*tmp_spawner, run(_, _, _)); |
1663 | + server_context = factory.create_server(tmp_spawner, connector); |
1664 | + } |
1665 | + |
1666 | + ASSERT_NE(server_context.get(), nullptr); |
1667 | +} |
1668 | + |
1669 | +TEST_F(SocketListeningServerTest, PassesMirSocketCorrectly) |
1670 | +{ |
1671 | + mir::X::GlobalSocketListeningServerSpawner factory; |
1672 | + |
1673 | + int const mir_fd{32}; |
1674 | + std::string const mir_connect_str{std::string("fd://") + std::to_string(mir_fd)}; |
1675 | + EXPECT_CALL(*connector, client_socket_fd()) |
1676 | + .WillOnce(Return(mir_fd)); |
1677 | + |
1678 | + auto server_context = factory.create_server(spawner, connector); |
1679 | + |
1680 | + server_context.get(); |
1681 | + |
1682 | + EXPECT_THAT(args, ContainsSubsequence(std::vector<std::string>{"-mirSocket", mir_connect_str})); |
1683 | + EXPECT_THAT(fds, Contains(Eq(mir_fd))); |
1684 | +} |
Please rethink class names ending in "er". That's almost always a bad decision, and an indication that a different cleaner design is possible...
http:// www.benhallbenh all.com/ 2013/01/ naming- objects- er-object- names/