Merge lp:~ted/ubuntu-app-launch/abstract-jobs into lp:ubuntu-app-launch/16.10
- abstract-jobs
- Merge into trunk.16.10
Proposed by
Ted Gould
Status: | Superseded |
---|---|
Proposed branch: | lp:~ted/ubuntu-app-launch/abstract-jobs |
Merge into: | lp:ubuntu-app-launch/16.10 |
Diff against target: |
6212 lines (+3149/-2127) 34 files modified
docs/index.rst (+40/-10) libubuntu-app-launch/CMakeLists.txt (+4/-0) libubuntu-app-launch/application-impl-base.cpp (+0/-766) libubuntu-app-launch/application-impl-base.h (+0/-69) libubuntu-app-launch/application-impl-click.cpp (+6/-19) libubuntu-app-launch/application-impl-legacy.cpp (+6/-32) libubuntu-app-launch/application-impl-libertine.cpp (+7/-17) libubuntu-app-launch/application-impl-snap.cpp (+6/-17) libubuntu-app-launch/application.cpp (+7/-0) libubuntu-app-launch/jobs-base.cpp (+392/-0) libubuntu-app-launch/jobs-base.h (+117/-0) libubuntu-app-launch/jobs-upstart.cpp (+899/-0) libubuntu-app-launch/jobs-upstart.h (+75/-0) libubuntu-app-launch/libubuntu-app-launch.map (+1/-0) libubuntu-app-launch/registry-impl.cpp (+494/-264) libubuntu-app-launch/registry-impl.h (+78/-25) libubuntu-app-launch/registry.cpp (+47/-54) libubuntu-app-launch/registry.h (+120/-14) libubuntu-app-launch/ubuntu-app-launch.cpp (+339/-413) tests/CMakeLists.txt (+1/-0) tests/failure-test.cc (+123/-96) tests/libual-cpp-test.cc (+186/-141) tools/CMakeLists.txt (+21/-2) tools/ubuntu-app-info.cpp (+18/-9) tools/ubuntu-app-launch.cpp (+56/-68) tools/ubuntu-app-list-pids.cpp (+7/-3) tools/ubuntu-app-list.cpp (+1/-1) tools/ubuntu-app-pid.cpp (+8/-4) tools/ubuntu-app-stop.cpp (+7/-3) tools/ubuntu-app-triplet.cpp (+1/-1) tools/ubuntu-app-watch.cpp (+61/-88) tools/ubuntu-helper-list.cpp (+1/-1) tools/ubuntu-helper-start.cpp (+10/-5) tools/ubuntu-helper-stop.cpp (+10/-5) |
To merge this branch: | bzr merge lp:~ted/ubuntu-app-launch/abstract-jobs |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Unity API Team | Pending | ||
Review via email: mp+309408@code.launchpad.net |
This proposal has been superseded by a proposal from 2016-11-10.
Commit message
Abstract out jobs backends and make Upstart one
Description of the change
To post a comment you must log in.
- 272. By Ted Gould
-
Make sure to grab numeric header as well
- 273. By Ted Gould
-
Merge in the app object signals branch
- 274. By Ted Gould
-
Merging in app object changes
- 275. By Ted Gould
-
Merge fail
- 276. By Ted Gould
-
Updated tests
- 277. By Ted Gould
-
Merging through trunk
- 278. By Ted Gould
-
Migrate tests to use pause/resume c++ signals
- 279. By Ted Gould
-
Merging app signals branch
- 280. By Ted Gould
-
Update to trunk
Unmerged revisions
- 280. By Ted Gould
-
Update to trunk
- 279. By Ted Gould
-
Merging app signals branch
- 278. By Ted Gould
-
Migrate tests to use pause/resume c++ signals
- 277. By Ted Gould
-
Merging through trunk
- 276. By Ted Gould
-
Updated tests
- 275. By Ted Gould
-
Merge fail
- 274. By Ted Gould
-
Merging in app object changes
- 273. By Ted Gould
-
Merge in the app object signals branch
- 272. By Ted Gould
-
Make sure to grab numeric header as well
- 271. By Ted Gould
-
Merge trunk
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'docs/index.rst' |
2 | --- docs/index.rst 2016-08-04 14:04:40 +0000 |
3 | +++ docs/index.rst 2016-11-10 21:58:30 +0000 |
4 | @@ -146,6 +146,46 @@ |
5 | :private-members: |
6 | :undoc-members: |
7 | |
8 | +Jobs Manager Base |
9 | +----------------- |
10 | + |
11 | +.. doxygenclass:: ubuntu::app_launch::jobs::manager::Base |
12 | + :project: libubuntu-app-launch |
13 | + :members: |
14 | + :protected-members: |
15 | + :private-members: |
16 | + :undoc-members: |
17 | + |
18 | +Jobs Instance Base |
19 | +------------------ |
20 | + |
21 | +.. doxygenclass:: ubuntu::app_launch::jobs::instance::Base |
22 | + :project: libubuntu-app-launch |
23 | + :members: |
24 | + :protected-members: |
25 | + :private-members: |
26 | + :undoc-members: |
27 | + |
28 | +Jobs Manager Upstart |
29 | +-------------------- |
30 | + |
31 | +.. doxygenclass:: ubuntu::app_launch::jobs::manager::Upstart |
32 | + :project: libubuntu-app-launch |
33 | + :members: |
34 | + :protected-members: |
35 | + :private-members: |
36 | + :undoc-members: |
37 | + |
38 | +Jobs Instance Upstart |
39 | +--------------------- |
40 | + |
41 | +.. doxygenclass:: ubuntu::app_launch::jobs::instance::Upstart |
42 | + :project: libubuntu-app-launch |
43 | + :members: |
44 | + :protected-members: |
45 | + :private-members: |
46 | + :undoc-members: |
47 | + |
48 | Registry Implementation |
49 | ----------------------- |
50 | |
51 | @@ -176,16 +216,6 @@ |
52 | :private-members: |
53 | :undoc-members: |
54 | |
55 | -Upstart Instance |
56 | ----------------- |
57 | - |
58 | -.. doxygenclass:: ubuntu::app_launch::app_impls::UpstartInstance |
59 | - :project: libubuntu-app-launch |
60 | - :members: |
61 | - :protected-members: |
62 | - :private-members: |
63 | - :undoc-members: |
64 | - |
65 | Quality |
66 | ======= |
67 | |
68 | |
69 | === modified file 'libubuntu-app-launch/CMakeLists.txt' |
70 | --- libubuntu-app-launch/CMakeLists.txt 2016-08-26 17:33:34 +0000 |
71 | +++ libubuntu-app-launch/CMakeLists.txt 2016-11-10 21:58:30 +0000 |
72 | @@ -52,6 +52,10 @@ |
73 | helper-impl-click.cpp |
74 | glib-thread.h |
75 | glib-thread.cpp |
76 | +jobs-base.h |
77 | +jobs-base.cpp |
78 | +jobs-upstart.h |
79 | +jobs-upstart.cpp |
80 | ) |
81 | |
82 | set(LAUNCHER_SOURCES |
83 | |
84 | === modified file 'libubuntu-app-launch/application-impl-base.cpp' |
85 | --- libubuntu-app-launch/application-impl-base.cpp 2016-10-03 23:54:20 +0000 |
86 | +++ libubuntu-app-launch/application-impl-base.cpp 2016-11-10 21:58:30 +0000 |
87 | @@ -23,8 +23,6 @@ |
88 | #include <map> |
89 | #include <numeric> |
90 | |
91 | -#include <upstart.h> |
92 | - |
93 | #include "application-impl-base.h" |
94 | #include "helpers.h" |
95 | #include "registry-impl.h" |
96 | @@ -97,770 +95,6 @@ |
97 | return retval; |
98 | } |
99 | |
100 | -/** Checks to see if we have a primary PID for the instance */ |
101 | -bool UpstartInstance::isRunning() |
102 | -{ |
103 | - return primaryPid() != 0; |
104 | -} |
105 | - |
106 | -/** Uses Upstart to get the primary PID of the instance using Upstart's |
107 | - DBus interface */ |
108 | -pid_t UpstartInstance::primaryPid() |
109 | -{ |
110 | - auto jobpath = registry_->impl->upstartJobPath(job_); |
111 | - if (jobpath.empty()) |
112 | - { |
113 | - g_debug("Unable to get a valid job path"); |
114 | - return 0; |
115 | - } |
116 | - |
117 | - return registry_->impl->thread.executeOnThread<pid_t>([this, &jobpath]() -> pid_t { |
118 | - GError* error = nullptr; |
119 | - |
120 | - std::string instancename = std::string(appId_); |
121 | - if (job_ != "application-click") |
122 | - { |
123 | - instancename += "-" + instance_; |
124 | - } |
125 | - |
126 | - g_debug("Getting instance by name: %s", instance_.c_str()); |
127 | - GVariant* vinstance_path = |
128 | - g_dbus_connection_call_sync(registry_->impl->_dbus.get(), /* connection */ |
129 | - DBUS_SERVICE_UPSTART, /* service */ |
130 | - jobpath.c_str(), /* object path */ |
131 | - DBUS_INTERFACE_UPSTART_JOB, /* iface */ |
132 | - "GetInstanceByName", /* method */ |
133 | - g_variant_new("(s)", instancename.c_str()), /* params */ |
134 | - G_VARIANT_TYPE("(o)"), /* return type */ |
135 | - G_DBUS_CALL_FLAGS_NONE, /* flags */ |
136 | - -1, /* timeout: default */ |
137 | - registry_->impl->thread.getCancellable().get(), /* cancellable */ |
138 | - &error); |
139 | - |
140 | - if (error != nullptr) |
141 | - { |
142 | - g_warning("Unable to get instance '%s' of job '%s': %s", instance_.c_str(), job_.c_str(), error->message); |
143 | - g_error_free(error); |
144 | - return 0; |
145 | - } |
146 | - |
147 | - /* Jump rope to make this into a C++ type */ |
148 | - std::string instance_path; |
149 | - gchar* cinstance_path = nullptr; |
150 | - g_variant_get(vinstance_path, "(o)", &cinstance_path); |
151 | - g_variant_unref(vinstance_path); |
152 | - if (cinstance_path != nullptr) |
153 | - { |
154 | - instance_path = cinstance_path; |
155 | - g_free(cinstance_path); |
156 | - } |
157 | - |
158 | - if (instance_path.empty()) |
159 | - { |
160 | - g_debug("No instance object for instance name: %s", instance_.c_str()); |
161 | - return 0; |
162 | - } |
163 | - |
164 | - GVariant* props_tuple = |
165 | - g_dbus_connection_call_sync(registry_->impl->_dbus.get(), /* connection */ |
166 | - DBUS_SERVICE_UPSTART, /* service */ |
167 | - instance_path.c_str(), /* object path */ |
168 | - "org.freedesktop.DBus.Properties", /* interface */ |
169 | - "GetAll", /* method */ |
170 | - g_variant_new("(s)", DBUS_INTERFACE_UPSTART_INSTANCE), /* params */ |
171 | - G_VARIANT_TYPE("(a{sv})"), /* return type */ |
172 | - G_DBUS_CALL_FLAGS_NONE, /* flags */ |
173 | - -1, /* timeout: default */ |
174 | - registry_->impl->thread.getCancellable().get(), /* cancellable */ |
175 | - &error); |
176 | - |
177 | - if (error != nullptr) |
178 | - { |
179 | - g_warning("Unable to name of properties '%s': %s", instance_path.c_str(), error->message); |
180 | - g_error_free(error); |
181 | - error = nullptr; |
182 | - return 0; |
183 | - } |
184 | - |
185 | - GVariant* props_dict = g_variant_get_child_value(props_tuple, 0); |
186 | - |
187 | - pid_t retval = 0; |
188 | - GVariant* processes = g_variant_lookup_value(props_dict, "processes", G_VARIANT_TYPE("a(si)")); |
189 | - if (processes != nullptr && g_variant_n_children(processes) > 0) |
190 | - { |
191 | - |
192 | - GVariant* first_entry = g_variant_get_child_value(processes, 0); |
193 | - GVariant* pidv = g_variant_get_child_value(first_entry, 1); |
194 | - |
195 | - retval = g_variant_get_int32(pidv); |
196 | - |
197 | - g_variant_unref(pidv); |
198 | - g_variant_unref(first_entry); |
199 | - } |
200 | - else |
201 | - { |
202 | - g_debug("Unable to get 'processes' from properties of instance at path: %s", instance_path.c_str()); |
203 | - } |
204 | - |
205 | - g_variant_unref(props_dict); |
206 | - |
207 | - return retval; |
208 | - }); |
209 | -} |
210 | - |
211 | -/** Generate the full name of the Upstart job for the job, the |
212 | - instance and how all those fit together. |
213 | - |
214 | - Handles the special case of application-click which isn't designed |
215 | - to have multi-instance apps. |
216 | -*/ |
217 | -std::string UpstartInstance::upstartJobPath() |
218 | -{ |
219 | - std::string path = job_ + "-" + std::string(appId_); |
220 | - if (job_ != "application-click") |
221 | - { |
222 | - path += "-"; |
223 | - } |
224 | - if (!instance_.empty()) |
225 | - { |
226 | - path += instance_; |
227 | - } |
228 | - |
229 | - return path; |
230 | -} |
231 | - |
232 | -/** Looks at the PIDs in the instance cgroup and checks to see if @pid |
233 | - is in the set. |
234 | - |
235 | - @param pid PID to look for |
236 | -*/ |
237 | -bool UpstartInstance::hasPid(pid_t pid) |
238 | -{ |
239 | - for (auto testpid : registry_->impl->pidsFromCgroup(upstartJobPath())) |
240 | - if (pid == testpid) |
241 | - return true; |
242 | - return false; |
243 | -} |
244 | - |
245 | -/** Gets the path to the log file for this instance */ |
246 | -std::string UpstartInstance::logPath() |
247 | -{ |
248 | - std::string logfile = upstartJobPath() + ".log"; |
249 | - |
250 | - gchar* cpath = g_build_filename(g_get_user_cache_dir(), "upstart", logfile.c_str(), nullptr); |
251 | - std::string path(cpath); |
252 | - g_free(cpath); |
253 | - |
254 | - return path; |
255 | -} |
256 | - |
257 | -/** Returns all the PIDs that are in the cgroup for this application */ |
258 | -std::vector<pid_t> UpstartInstance::pids() |
259 | -{ |
260 | - auto pids = registry_->impl->pidsFromCgroup(upstartJobPath()); |
261 | - g_debug("Got %d PIDs for AppID '%s'", int(pids.size()), std::string(appId_).c_str()); |
262 | - return pids; |
263 | -} |
264 | - |
265 | -/** Pauses this application by sending SIGSTOP to all the PIDs in the |
266 | - cgroup and tells Zeitgeist that we've left the application. */ |
267 | -void UpstartInstance::pause() |
268 | -{ |
269 | - g_debug("Pausing application: %s", std::string(appId_).c_str()); |
270 | - registry_->impl->zgSendEvent(appId_, ZEITGEIST_ZG_LEAVE_EVENT); |
271 | - |
272 | - auto pids = forAllPids([this](pid_t pid) { |
273 | - auto oomval = oom::paused(); |
274 | - g_debug("Pausing PID: %d (%d)", pid, int(oomval)); |
275 | - signalToPid(pid, SIGSTOP); |
276 | - oomValueToPid(pid, oomval); |
277 | - }); |
278 | - |
279 | - pidListToDbus(pids, "ApplicationPaused"); |
280 | -} |
281 | - |
282 | -/** Resumes this application by sending SIGCONT to all the PIDs in the |
283 | - cgroup and tells Zeitgeist that we're accessing the application. */ |
284 | -void UpstartInstance::resume() |
285 | -{ |
286 | - g_debug("Resuming application: %s", std::string(appId_).c_str()); |
287 | - registry_->impl->zgSendEvent(appId_, ZEITGEIST_ZG_ACCESS_EVENT); |
288 | - |
289 | - auto pids = forAllPids([this](pid_t pid) { |
290 | - auto oomval = oom::focused(); |
291 | - g_debug("Resuming PID: %d (%d)", pid, int(oomval)); |
292 | - signalToPid(pid, SIGCONT); |
293 | - oomValueToPid(pid, oomval); |
294 | - }); |
295 | - |
296 | - pidListToDbus(pids, "ApplicationResumed"); |
297 | -} |
298 | - |
299 | -/** Stops this instance by asking Upstart to stop it. Upstart will then |
300 | - send a SIGTERM and five seconds later start killing things. */ |
301 | -void UpstartInstance::stop() |
302 | -{ |
303 | - if (!registry_->impl->thread.executeOnThread<bool>([this]() { |
304 | - |
305 | - g_debug("Stopping job %s app_id %s instance_id %s", job_.c_str(), std::string(appId_).c_str(), |
306 | - instance_.c_str()); |
307 | - |
308 | - auto jobpath = registry_->impl->upstartJobPath(job_); |
309 | - if (jobpath.empty()) |
310 | - { |
311 | - throw new std::runtime_error("Unable to get job path for Upstart job '" + job_ + "'"); |
312 | - } |
313 | - |
314 | - GVariantBuilder builder; |
315 | - g_variant_builder_init(&builder, G_VARIANT_TYPE_TUPLE); |
316 | - g_variant_builder_open(&builder, G_VARIANT_TYPE_ARRAY); |
317 | - |
318 | - g_variant_builder_add_value( |
319 | - &builder, g_variant_new_take_string(g_strdup_printf("APP_ID=%s", std::string(appId_).c_str()))); |
320 | - |
321 | - if (!instance_.empty()) |
322 | - { |
323 | - g_variant_builder_add_value( |
324 | - &builder, g_variant_new_take_string(g_strdup_printf("INSTANCE_ID=%s", instance_.c_str()))); |
325 | - } |
326 | - |
327 | - g_variant_builder_close(&builder); |
328 | - g_variant_builder_add_value(&builder, g_variant_new_boolean(FALSE)); /* wait */ |
329 | - |
330 | - GError* error = nullptr; |
331 | - GVariant* stop_variant = |
332 | - g_dbus_connection_call_sync(registry_->impl->_dbus.get(), /* Dbus */ |
333 | - DBUS_SERVICE_UPSTART, /* Upstart name */ |
334 | - jobpath.c_str(), /* path */ |
335 | - DBUS_INTERFACE_UPSTART_JOB, /* interface */ |
336 | - "Stop", /* method */ |
337 | - g_variant_builder_end(&builder), /* params */ |
338 | - nullptr, /* return */ |
339 | - G_DBUS_CALL_FLAGS_NONE, /* flags */ |
340 | - -1, /* timeout: default */ |
341 | - registry_->impl->thread.getCancellable().get(), /* cancellable */ |
342 | - &error); /* error (hopefully not) */ |
343 | - |
344 | - g_clear_pointer(&stop_variant, g_variant_unref); |
345 | - |
346 | - if (error != nullptr) |
347 | - { |
348 | - g_warning("Unable to stop job %s app_id %s instance_id %s: %s", job_.c_str(), |
349 | - std::string(appId_).c_str(), instance_.c_str(), error->message); |
350 | - g_error_free(error); |
351 | - return false; |
352 | - } |
353 | - |
354 | - return true; |
355 | - })) |
356 | - { |
357 | - g_warning("Unable to stop Upstart instance"); |
358 | - } |
359 | -} |
360 | - |
361 | -/** Sets the OOM adjustment by getting the list of PIDs and writing |
362 | - the value to each of their files in proc |
363 | - |
364 | - \param score OOM Score to set |
365 | -*/ |
366 | -void UpstartInstance::setOomAdjustment(const oom::Score score) |
367 | -{ |
368 | - forAllPids([this, &score](pid_t pid) { oomValueToPid(pid, score); }); |
369 | -} |
370 | - |
371 | -/** Figures out the path to the primary PID of the application and |
372 | - then reads its OOM adjustment file. */ |
373 | -const oom::Score UpstartInstance::getOomAdjustment() |
374 | -{ |
375 | - auto pid = primaryPid(); |
376 | - if (pid == 0) |
377 | - { |
378 | - throw std::runtime_error("No PID for application: " + std::string(appId_)); |
379 | - } |
380 | - |
381 | - auto path = pidToOomPath(pid); |
382 | - GError* error = nullptr; |
383 | - gchar* content = nullptr; |
384 | - |
385 | - g_file_get_contents(path.c_str(), /* path */ |
386 | - &content, /* data */ |
387 | - nullptr, /* size */ |
388 | - &error); /* error */ |
389 | - |
390 | - if (error != nullptr) |
391 | - { |
392 | - auto serror = std::shared_ptr<GError>(error, g_error_free); |
393 | - throw std::runtime_error("Unable to access OOM value for '" + std::string(appId_) + "' primary PID '" + |
394 | - std::to_string(pid) + "' because: " + serror->message); |
395 | - } |
396 | - |
397 | - auto score = static_cast<oom::Score>(std::atoi(content)); |
398 | - g_free(content); |
399 | - return score; |
400 | -} |
401 | - |
402 | -/** Go through the list of PIDs calling a function and handling |
403 | - the issue with getting PIDs being a racey condition. |
404 | - |
405 | - \param eachPid Function to run on each PID |
406 | -*/ |
407 | -std::vector<pid_t> UpstartInstance::forAllPids(std::function<void(pid_t)> eachPid) |
408 | -{ |
409 | - std::set<pid_t> seenPids; |
410 | - bool added = true; |
411 | - |
412 | - while (added) |
413 | - { |
414 | - added = false; |
415 | - auto pidlist = pids(); |
416 | - for (auto pid : pidlist) |
417 | - { |
418 | - if (seenPids.insert(pid).second) |
419 | - { |
420 | - eachPid(pid); |
421 | - added = true; |
422 | - } |
423 | - } |
424 | - } |
425 | - |
426 | - return std::vector<pid_t>(seenPids.begin(), seenPids.end()); |
427 | -} |
428 | - |
429 | -/** Sends a signal to a PID with a warning if we can't send it. |
430 | - We could throw an exception, but we can't handle it usefully anyway |
431 | - |
432 | - \param pid PID to send the signal to |
433 | - \param signal signal to send |
434 | -*/ |
435 | -void UpstartInstance::signalToPid(pid_t pid, int signal) |
436 | -{ |
437 | - if (-1 == kill(pid, signal)) |
438 | - { |
439 | - /* While that didn't work, we still want to try as many as we can */ |
440 | - g_warning("Unable to send signal %d to pid %d", signal, pid); |
441 | - } |
442 | -} |
443 | - |
444 | -/** Get the path to the PID's OOM adjust path, with allowing for an |
445 | - override for testing using the environment variable |
446 | - UBUNTU_APP_LAUNCH_OOM_PROC_PATH |
447 | - |
448 | - \param pid PID to build path for |
449 | -*/ |
450 | -std::string UpstartInstance::pidToOomPath(pid_t pid) |
451 | -{ |
452 | - static std::string procpath; |
453 | - if (G_UNLIKELY(procpath.empty())) |
454 | - { |
455 | - /* Set by the test suite, probably not anyone else */ |
456 | - auto envvar = g_getenv("UBUNTU_APP_LAUNCH_OOM_PROC_PATH"); |
457 | - if (G_LIKELY(envvar == nullptr)) |
458 | - procpath = "/proc"; |
459 | - else |
460 | - procpath = envvar; |
461 | - } |
462 | - |
463 | - gchar* gpath = g_build_filename(procpath.c_str(), std::to_string(pid).c_str(), "oom_score_adj", nullptr); |
464 | - std::string path = gpath; |
465 | - g_free(gpath); |
466 | - return path; |
467 | -} |
468 | - |
469 | -/** Writes an OOM value to proc, assuming we have a string |
470 | - in the outer loop |
471 | - |
472 | - \param pid PID to change the OOM value of |
473 | - \param oomvalue OOM value to set |
474 | -*/ |
475 | -void UpstartInstance::oomValueToPid(pid_t pid, const oom::Score oomvalue) |
476 | -{ |
477 | - auto oomstr = std::to_string(static_cast<std::int32_t>(oomvalue)); |
478 | - auto path = pidToOomPath(pid); |
479 | - FILE* adj = fopen(path.c_str(), "w"); |
480 | - int openerr = errno; |
481 | - |
482 | - if (adj == nullptr) |
483 | - { |
484 | - switch (openerr) |
485 | - { |
486 | - case ENOENT: |
487 | - /* ENOENT happens a fair amount because of races, so it's not |
488 | - worth printing a warning about */ |
489 | - return; |
490 | - case EACCES: |
491 | - { |
492 | - /* We can get this error when trying to set the OOM value on |
493 | - Oxide renderers because they're started by the sandbox and |
494 | - don't have their adjustment value available for us to write. |
495 | - We have a helper to deal with this, but it's kinda expensive |
496 | - so we only use it when we have to. */ |
497 | - oomValueToPidHelper(pid, oomvalue); |
498 | - return; |
499 | - } |
500 | - default: |
501 | - g_warning("Unable to set OOM value for '%d' to '%s': %s", int(pid), oomstr.c_str(), |
502 | - std::strerror(openerr)); |
503 | - return; |
504 | - } |
505 | - } |
506 | - |
507 | - size_t writesize = fwrite(oomstr.c_str(), 1, oomstr.size(), adj); |
508 | - int writeerr = errno; |
509 | - fclose(adj); |
510 | - |
511 | - if (writesize == oomstr.size()) |
512 | - return; |
513 | - |
514 | - if (writeerr != 0) |
515 | - g_warning("Unable to set OOM value for '%d' to '%s': %s", int(pid), oomstr.c_str(), strerror(writeerr)); |
516 | - else |
517 | - /* No error, but yet, wrong size. Not sure, what could cause this. */ |
518 | - g_debug("Unable to set OOM value for '%d' to '%s': Wrote %d bytes", int(pid), oomstr.c_str(), int(writesize)); |
519 | -} |
520 | - |
521 | -/** Use a setuid root helper for setting the oom value of |
522 | - Chromium instances |
523 | - |
524 | - \param pid PID to change the OOM value of |
525 | - \param oomvalue OOM value to set |
526 | -*/ |
527 | -void UpstartInstance::oomValueToPidHelper(pid_t pid, const oom::Score oomvalue) |
528 | -{ |
529 | - GError* error = nullptr; |
530 | - std::string oomstr = std::to_string(static_cast<std::int32_t>(oomvalue)); |
531 | - std::string pidstr = std::to_string(pid); |
532 | - std::array<const char*, 4> args = {OOM_HELPER, pidstr.c_str(), oomstr.c_str(), nullptr}; |
533 | - |
534 | - g_debug("Excuting OOM Helper (pid: %d, score: %d): %s", int(pid), int(oomvalue), |
535 | - std::accumulate(args.begin(), args.end(), std::string{}, |
536 | - [](const std::string& instr, const char* output) -> std::string { |
537 | - if (instr.empty()) |
538 | - { |
539 | - return output; |
540 | - } |
541 | - else if (output != nullptr) |
542 | - { |
543 | - return instr + " " + std::string(output); |
544 | - } |
545 | - else |
546 | - { |
547 | - return instr; |
548 | - } |
549 | - }) |
550 | - .c_str()); |
551 | - |
552 | - g_spawn_async(nullptr, /* working dir */ |
553 | - (char**)(args.data()), /* args */ |
554 | - nullptr, /* env */ |
555 | - G_SPAWN_DEFAULT, /* flags */ |
556 | - nullptr, /* child setup */ |
557 | - nullptr, /* child setup userdata*/ |
558 | - nullptr, /* pid */ |
559 | - &error); /* error */ |
560 | - |
561 | - if (error != nullptr) |
562 | - { |
563 | - g_warning("Unable to launch OOM helper '" OOM_HELPER "' on PID '%d': %s", pid, error->message); |
564 | - g_error_free(error); |
565 | - return; |
566 | - } |
567 | -} |
568 | - |
569 | -/** Send a signal that we've change the application. Do this on the |
570 | - registry thread in an idle so that we don't block anyone. |
571 | - |
572 | - \param pids List of PIDs to turn into variants to send |
573 | - \param signal Name of the DBus signal to send |
574 | -*/ |
575 | -void UpstartInstance::pidListToDbus(const std::vector<pid_t>& pids, const std::string& signal) |
576 | -{ |
577 | - auto registry = registry_; |
578 | - auto lappid = appId_; |
579 | - |
580 | - registry_->impl->thread.executeOnThread([registry, lappid, pids, signal] { |
581 | - auto vpids = std::shared_ptr<GVariant>( |
582 | - [pids]() { |
583 | - GVariant* pidarray = nullptr; |
584 | - |
585 | - if (pids.empty()) |
586 | - { |
587 | - pidarray = g_variant_new_array(G_VARIANT_TYPE_UINT64, nullptr, 0); |
588 | - g_variant_ref_sink(pidarray); |
589 | - return pidarray; |
590 | - } |
591 | - |
592 | - GVariantBuilder builder; |
593 | - g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); |
594 | - for (auto pid : pids) |
595 | - { |
596 | - g_variant_builder_add_value(&builder, g_variant_new_uint64(pid)); |
597 | - } |
598 | - |
599 | - pidarray = g_variant_builder_end(&builder); |
600 | - g_variant_ref_sink(pidarray); |
601 | - return pidarray; |
602 | - }(), |
603 | - [](GVariant* var) { g_variant_unref(var); }); |
604 | - |
605 | - GVariantBuilder params; |
606 | - g_variant_builder_init(¶ms, G_VARIANT_TYPE_TUPLE); |
607 | - g_variant_builder_add_value(¶ms, g_variant_new_string(std::string(lappid).c_str())); |
608 | - g_variant_builder_add_value(¶ms, vpids.get()); |
609 | - |
610 | - GError* error = nullptr; |
611 | - g_dbus_connection_emit_signal(registry->impl->_dbus.get(), /* bus */ |
612 | - nullptr, /* destination */ |
613 | - "/", /* path */ |
614 | - "com.canonical.UbuntuAppLaunch", /* interface */ |
615 | - signal.c_str(), /* signal */ |
616 | - g_variant_builder_end(¶ms), /* params, the same */ |
617 | - &error); /* error */ |
618 | - |
619 | - if (error != nullptr) |
620 | - { |
621 | - g_warning("Unable to emit signal '%s' for appid '%s': %s", signal.c_str(), std::string(lappid).c_str(), |
622 | - error->message); |
623 | - g_error_free(error); |
624 | - } |
625 | - else |
626 | - { |
627 | - g_debug("Emmitted '%s' to DBus", signal.c_str()); |
628 | - } |
629 | - }); |
630 | -} |
631 | - |
632 | -/** Create a new Upstart Instance object that can track the job and |
633 | - get information about it. |
634 | - |
635 | - \param appId Application ID |
636 | - \param job Upstart job name |
637 | - \param instance Upstart instance name |
638 | - \param urls URLs sent to the application (only on launch today) |
639 | - \param registry Registry of persistent connections to use |
640 | -*/ |
641 | -UpstartInstance::UpstartInstance(const AppID& appId, |
642 | - const std::string& job, |
643 | - const std::string& instance, |
644 | - const std::vector<Application::URL>& urls, |
645 | - const std::shared_ptr<Registry>& registry) |
646 | - : appId_(appId) |
647 | - , job_(job) |
648 | - , instance_(instance) |
649 | - , urls_(urls) |
650 | - , registry_(registry) |
651 | -{ |
652 | - g_debug("Creating a new UpstartInstance for '%s' instance '%s'", std::string(appId_).c_str(), instance.c_str()); |
653 | -} |
654 | - |
655 | -/** Reformat a C++ vector of URLs into a C GStrv of strings |
656 | - |
657 | - \param urls Vector of URLs to make into C strings |
658 | -*/ |
659 | -std::shared_ptr<gchar*> UpstartInstance::urlsToStrv(const std::vector<Application::URL>& urls) |
660 | -{ |
661 | - if (urls.empty()) |
662 | - { |
663 | - return {}; |
664 | - } |
665 | - |
666 | - auto array = g_array_new(TRUE, FALSE, sizeof(gchar*)); |
667 | - |
668 | - for (auto url : urls) |
669 | - { |
670 | - auto str = g_strdup(url.value().c_str()); |
671 | - g_debug("Converting URL: %s", str); |
672 | - g_array_append_val(array, str); |
673 | - } |
674 | - |
675 | - return std::shared_ptr<gchar*>((gchar**)g_array_free(array, FALSE), g_strfreev); |
676 | -} |
677 | - |
678 | -/** Small helper that we can new/delete to work better with C stuff */ |
679 | -struct StartCHelper |
680 | -{ |
681 | - std::shared_ptr<UpstartInstance> ptr; |
682 | -}; |
683 | - |
684 | -/** Callback from starting an application. It checks to see whether the |
685 | - app is already running. If it is already running then we need to send |
686 | - the URLs to it via DBus. |
687 | - |
688 | - \param obj The GDBusConnection object |
689 | - \param res Async result object |
690 | - \param user_data A pointer to a StartCHelper structure |
691 | -*/ |
692 | -void UpstartInstance::application_start_cb(GObject* obj, GAsyncResult* res, gpointer user_data) |
693 | -{ |
694 | - auto data = static_cast<StartCHelper*>(user_data); |
695 | - GError* error{nullptr}; |
696 | - GVariant* result{nullptr}; |
697 | - |
698 | - tracepoint(ubuntu_app_launch, libual_start_message_callback, std::string(data->ptr->appId_).c_str()); |
699 | - |
700 | - g_debug("Started Message Callback: %s", std::string(data->ptr->appId_).c_str()); |
701 | - |
702 | - result = g_dbus_connection_call_finish(G_DBUS_CONNECTION(obj), res, &error); |
703 | - |
704 | - g_clear_pointer(&result, g_variant_unref); |
705 | - |
706 | - if (error != nullptr) |
707 | - { |
708 | - if (g_dbus_error_is_remote_error(error)) |
709 | - { |
710 | - gchar* remote_error = g_dbus_error_get_remote_error(error); |
711 | - g_debug("Remote error: %s", remote_error); |
712 | - if (g_strcmp0(remote_error, "com.ubuntu.Upstart0_6.Error.AlreadyStarted") == 0) |
713 | - { |
714 | - auto urls = urlsToStrv(data->ptr->urls_); |
715 | - second_exec(data->ptr->registry_->impl->_dbus.get(), /* DBus */ |
716 | - data->ptr->registry_->impl->thread.getCancellable().get(), /* cancellable */ |
717 | - data->ptr->primaryPid(), /* primary pid */ |
718 | - std::string(data->ptr->appId_).c_str(), /* appid */ |
719 | - urls.get()); /* urls */ |
720 | - } |
721 | - |
722 | - g_free(remote_error); |
723 | - } |
724 | - else |
725 | - { |
726 | - g_warning("Unable to emit event to start application: %s", error->message); |
727 | - } |
728 | - g_error_free(error); |
729 | - } |
730 | - |
731 | - delete data; |
732 | -} |
733 | - |
734 | -/** Launch an application and create a new UpstartInstance object to track |
735 | - its progress. |
736 | - |
737 | - \param appId Application ID |
738 | - \param job Upstart job name |
739 | - \param instance Upstart instance name |
740 | - \param urls URLs sent to the application (only on launch today) |
741 | - \param registry Registry of persistent connections to use |
742 | - \param mode Whether or not to setup the environment for testing |
743 | - \param getenv A function to get additional environment variable when appropriate |
744 | -*/ |
745 | -std::shared_ptr<UpstartInstance> UpstartInstance::launch( |
746 | - const AppID& appId, |
747 | - const std::string& job, |
748 | - const std::string& instance, |
749 | - const std::vector<Application::URL>& urls, |
750 | - const std::shared_ptr<Registry>& registry, |
751 | - launchMode mode, |
752 | - std::function<std::list<std::pair<std::string, std::string>>(void)>& getenv) |
753 | -{ |
754 | - if (appId.empty()) |
755 | - return {}; |
756 | - |
757 | - return registry->impl->thread.executeOnThread<std::shared_ptr<UpstartInstance>>( |
758 | - [&]() -> std::shared_ptr<UpstartInstance> { |
759 | - std::string appIdStr{appId}; |
760 | - g_debug("Initializing params for an new UpstartInstance for: %s", appIdStr.c_str()); |
761 | - |
762 | - tracepoint(ubuntu_app_launch, libual_start, appIdStr.c_str()); |
763 | - |
764 | - int timeout = 1; |
765 | - if (ubuntu::app_launch::Registry::Impl::isWatchingAppStarting()) |
766 | - { |
767 | - timeout = 0; |
768 | - } |
769 | - |
770 | - auto handshake = starting_handshake_start(appIdStr.c_str(), timeout); |
771 | - if (handshake == nullptr) |
772 | - { |
773 | - g_warning("Unable to setup starting handshake"); |
774 | - } |
775 | - |
776 | - /* Figure out the DBus path for the job */ |
777 | - auto jobpath = registry->impl->upstartJobPath(job); |
778 | - |
779 | - /* Build up our environment */ |
780 | - auto env = getenv(); |
781 | - |
782 | - env.emplace_back(std::make_pair("APP_ID", appIdStr)); /* Application ID */ |
783 | - env.emplace_back(std::make_pair("APP_LAUNCHER_PID", std::to_string(getpid()))); /* Who we are, for bugs */ |
784 | - |
785 | - if (!urls.empty()) |
786 | - { |
787 | - auto accumfunc = [](const std::string& prev, Application::URL thisurl) -> std::string { |
788 | - gchar* gescaped = g_shell_quote(thisurl.value().c_str()); |
789 | - std::string escaped; |
790 | - if (gescaped != nullptr) |
791 | - { |
792 | - escaped = gescaped; |
793 | - g_free(gescaped); |
794 | - } |
795 | - else |
796 | - { |
797 | - g_warning("Unable to escape URL: %s", thisurl.value().c_str()); |
798 | - return prev; |
799 | - } |
800 | - |
801 | - if (prev.empty()) |
802 | - { |
803 | - return escaped; |
804 | - } |
805 | - else |
806 | - { |
807 | - return prev + " " + escaped; |
808 | - } |
809 | - }; |
810 | - auto urlstring = std::accumulate(urls.begin(), urls.end(), std::string{}, accumfunc); |
811 | - env.emplace_back(std::make_pair("APP_URIS", urlstring)); |
812 | - } |
813 | - |
814 | - if (mode == launchMode::TEST) |
815 | - { |
816 | - env.emplace_back(std::make_pair("QT_LOAD_TESTABILITY", "1")); |
817 | - } |
818 | - |
819 | - /* Convert to GVariant */ |
820 | - GVariantBuilder builder; |
821 | - g_variant_builder_init(&builder, G_VARIANT_TYPE_TUPLE); |
822 | - |
823 | - g_variant_builder_open(&builder, G_VARIANT_TYPE_ARRAY); |
824 | - |
825 | - for (const auto& envvar : env) |
826 | - { |
827 | - g_variant_builder_add_value(&builder, g_variant_new_take_string(g_strdup_printf( |
828 | - "%s=%s", envvar.first.c_str(), envvar.second.c_str()))); |
829 | - } |
830 | - |
831 | - g_variant_builder_close(&builder); |
832 | - g_variant_builder_add_value(&builder, g_variant_new_boolean(TRUE)); |
833 | - |
834 | - auto retval = std::make_shared<UpstartInstance>(appId, job, instance, urls, registry); |
835 | - auto chelper = new StartCHelper{}; |
836 | - chelper->ptr = retval; |
837 | - |
838 | - tracepoint(ubuntu_app_launch, handshake_wait, appIdStr.c_str()); |
839 | - starting_handshake_wait(handshake); |
840 | - tracepoint(ubuntu_app_launch, handshake_complete, appIdStr.c_str()); |
841 | - |
842 | - /* Call the job start function */ |
843 | - g_debug("Asking Upstart to start task for: %s", appIdStr.c_str()); |
844 | - g_dbus_connection_call(registry->impl->_dbus.get(), /* bus */ |
845 | - DBUS_SERVICE_UPSTART, /* service name */ |
846 | - jobpath.c_str(), /* Path */ |
847 | - DBUS_INTERFACE_UPSTART_JOB, /* interface */ |
848 | - "Start", /* method */ |
849 | - g_variant_builder_end(&builder), /* params */ |
850 | - nullptr, /* return */ |
851 | - G_DBUS_CALL_FLAGS_NONE, /* flags */ |
852 | - -1, /* default timeout */ |
853 | - registry->impl->thread.getCancellable().get(), /* cancellable */ |
854 | - application_start_cb, /* callback */ |
855 | - chelper /* object */ |
856 | - ); |
857 | - |
858 | - tracepoint(ubuntu_app_launch, libual_start_message_sent, appIdStr.c_str()); |
859 | - |
860 | - return retval; |
861 | - }); |
862 | -} |
863 | - |
864 | } // namespace app_impls |
865 | } // namespace app_launch |
866 | } // namespace ubuntu |
867 | |
868 | === modified file 'libubuntu-app-launch/application-impl-base.h' |
869 | --- libubuntu-app-launch/application-impl-base.h 2016-09-23 22:30:51 +0000 |
870 | +++ libubuntu-app-launch/application-impl-base.h 2016-11-10 21:58:30 +0000 |
871 | @@ -51,75 +51,6 @@ |
872 | const std::string& pkgdir); |
873 | }; |
874 | |
875 | -/** An object that represents an instance of a job on Upstart. This |
876 | - then implements everything needed by the instance interface. Most |
877 | - applications tie into this today and use it as the backend for |
878 | - their instances. */ |
879 | -class UpstartInstance : public Application::Instance |
880 | -{ |
881 | -public: |
882 | - explicit UpstartInstance(const AppID& appId, |
883 | - const std::string& job, |
884 | - const std::string& instance, |
885 | - const std::vector<Application::URL>& urls, |
886 | - const std::shared_ptr<Registry>& registry); |
887 | - |
888 | - /* Query lifecycle */ |
889 | - bool isRunning() override; |
890 | - pid_t primaryPid() override; |
891 | - bool hasPid(pid_t pid) override; |
892 | - std::string logPath() override; |
893 | - std::vector<pid_t> pids() override; |
894 | - |
895 | - /* Manage lifecycle */ |
896 | - void pause() override; |
897 | - void resume() override; |
898 | - void stop() override; |
899 | - |
900 | - /* OOM Functions */ |
901 | - void setOomAdjustment(const oom::Score score) override; |
902 | - const oom::Score getOomAdjustment() override; |
903 | - |
904 | - /** Flag for whether we should include the testing environment variables */ |
905 | - enum class launchMode |
906 | - { |
907 | - STANDARD, /**< Standard variable set */ |
908 | - TEST /**< Include testing environment vars */ |
909 | - }; |
910 | - static std::shared_ptr<UpstartInstance> launch( |
911 | - const AppID& appId, |
912 | - const std::string& job, |
913 | - const std::string& instance, |
914 | - const std::vector<Application::URL>& urls, |
915 | - const std::shared_ptr<Registry>& registry, |
916 | - launchMode mode, |
917 | - std::function<std::list<std::pair<std::string, std::string>>(void)>& getenv); |
918 | - |
919 | -private: |
920 | - /** Application ID */ |
921 | - const AppID appId_; |
922 | - /** Upstart job name */ |
923 | - const std::string job_; |
924 | - /** Instance ID environment value, empty if none */ |
925 | - const std::string instance_; |
926 | - /** The URLs that this was launched for. Only valid on launched jobs, we |
927 | - should look at perhaps changing that. */ |
928 | - std::vector<Application::URL> urls_; |
929 | - /** A link to the registry we're using for connections */ |
930 | - std::shared_ptr<Registry> registry_; |
931 | - |
932 | - std::vector<pid_t> forAllPids(std::function<void(pid_t)> eachPid); |
933 | - void signalToPid(pid_t pid, int signal); |
934 | - std::string pidToOomPath(pid_t pid); |
935 | - void oomValueToPid(pid_t pid, const oom::Score oomvalue); |
936 | - void oomValueToPidHelper(pid_t pid, const oom::Score oomvalue); |
937 | - void pidListToDbus(const std::vector<pid_t>& pids, const std::string& signal); |
938 | - std::string upstartJobPath(); |
939 | - |
940 | - static std::shared_ptr<gchar*> urlsToStrv(const std::vector<Application::URL>& urls); |
941 | - static void application_start_cb(GObject* obj, GAsyncResult* res, gpointer user_data); |
942 | -}; |
943 | - |
944 | } // namespace app_impls |
945 | } // namespace app_launch |
946 | } // namespace ubuntu |
947 | |
948 | === modified file 'libubuntu-app-launch/application-impl-click.cpp' |
949 | --- libubuntu-app-launch/application-impl-click.cpp 2016-10-03 23:54:20 +0000 |
950 | +++ libubuntu-app-launch/application-impl-click.cpp 2016-11-10 21:58:30 +0000 |
951 | @@ -323,21 +323,8 @@ |
952 | |
953 | std::vector<std::shared_ptr<Application::Instance>> Click::instances() |
954 | { |
955 | - std::vector<std::shared_ptr<Instance>> vect; |
956 | - std::string sappid = appId(); |
957 | - |
958 | - for (auto instancename : _registry->impl->upstartInstancesForJob("application-click")) |
959 | - { |
960 | - /* There an be only one, but we want to make sure it is |
961 | - there or return an empty vector */ |
962 | - if (sappid == instancename) |
963 | - { |
964 | - vect.emplace_back(std::make_shared<UpstartInstance>(appId(), "application-click", std::string{}, |
965 | - std::vector<Application::URL>{}, _registry)); |
966 | - break; |
967 | - } |
968 | - } |
969 | - return vect; |
970 | + auto vbase = _registry->impl->jobs->instances(appId(), "application-click"); |
971 | + return std::vector<std::shared_ptr<Application::Instance>>(vbase.begin(), vbase.end()); |
972 | } |
973 | |
974 | /** Grabs all the environment variables for the application to |
975 | @@ -361,15 +348,15 @@ |
976 | std::shared_ptr<Application::Instance> Click::launch(const std::vector<Application::URL>& urls) |
977 | { |
978 | std::function<std::list<std::pair<std::string, std::string>>(void)> envfunc = [this]() { return launchEnv(); }; |
979 | - return UpstartInstance::launch(appId(), "application-click", {}, urls, _registry, |
980 | - UpstartInstance::launchMode::STANDARD, envfunc); |
981 | + return _registry->impl->jobs->launch(appId(), "application-click", {}, urls, jobs::manager::launchMode::STANDARD, |
982 | + envfunc); |
983 | } |
984 | |
985 | std::shared_ptr<Application::Instance> Click::launchTest(const std::vector<Application::URL>& urls) |
986 | { |
987 | std::function<std::list<std::pair<std::string, std::string>>(void)> envfunc = [this]() { return launchEnv(); }; |
988 | - return UpstartInstance::launch(appId(), "application-click", {}, urls, _registry, UpstartInstance::launchMode::TEST, |
989 | - envfunc); |
990 | + return _registry->impl->jobs->launch(appId(), "application-click", {}, urls, jobs::manager::launchMode::TEST, |
991 | + envfunc); |
992 | } |
993 | |
994 | } // namespace app_impls |
995 | |
996 | === modified file 'libubuntu-app-launch/application-impl-legacy.cpp' |
997 | --- libubuntu-app-launch/application-impl-legacy.cpp 2016-10-03 23:54:20 +0000 |
998 | +++ libubuntu-app-launch/application-impl-legacy.cpp 2016-11-10 21:58:30 +0000 |
999 | @@ -33,9 +33,6 @@ |
1000 | /** Path that snapd puts desktop files, we don't want to read those directly |
1001 | in the Legacy backend. We want to use the snap backend. */ |
1002 | const std::string snappyDesktopPath{"/var/lib/snapd"}; |
1003 | -/** Special characters that could be an application name that |
1004 | - would activate in a regex */ |
1005 | -const static std::regex regexCharacters("([\\.\\-])"); |
1006 | |
1007 | /*********************************** |
1008 | Prototypes |
1009 | @@ -77,14 +74,6 @@ |
1010 | { |
1011 | throw std::runtime_error{"Looking like a legacy app, but should be a Snap: " + appname.value()}; |
1012 | } |
1013 | - |
1014 | - /* Build a regex that'll match instances of the applications which |
1015 | - roughly looks like: $(appid)-2345345 |
1016 | - |
1017 | - It is important to filter out the special characters that are in |
1018 | - the appid. |
1019 | - */ |
1020 | - instanceRegex_ = std::regex("^(?:" + std::regex_replace(_appname.value(), regexCharacters, "\\$&") + ")\\-(\\d*)$"); |
1021 | } |
1022 | |
1023 | std::tuple<std::string, std::shared_ptr<GKeyFile>, std::string> keyfileForApp(const AppID::AppName& name) |
1024 | @@ -291,23 +280,8 @@ |
1025 | |
1026 | std::vector<std::shared_ptr<Application::Instance>> Legacy::instances() |
1027 | { |
1028 | - std::vector<std::shared_ptr<Instance>> vect; |
1029 | - auto startsWith = std::string(appId()) + "-"; |
1030 | - |
1031 | - for (auto instance : _registry->impl->upstartInstancesForJob("application-legacy")) |
1032 | - { |
1033 | - std::smatch instanceMatch; |
1034 | - g_debug("Looking at legacy instance: %s", instance.c_str()); |
1035 | - if (std::regex_match(instance, instanceMatch, instanceRegex_)) |
1036 | - { |
1037 | - vect.emplace_back(std::make_shared<UpstartInstance>(appId(), "application-legacy", instanceMatch[1].str(), |
1038 | - std::vector<Application::URL>{}, _registry)); |
1039 | - } |
1040 | - } |
1041 | - |
1042 | - g_debug("Legacy app '%s' has %d instances", std::string(appId()).c_str(), int(vect.size())); |
1043 | - |
1044 | - return vect; |
1045 | + auto vbase = _registry->impl->jobs->instances(appId(), "application-legacy"); |
1046 | + return std::vector<std::shared_ptr<Application::Instance>>(vbase.begin(), vbase.end()); |
1047 | } |
1048 | |
1049 | /** Grabs all the environment for a legacy app. Mostly this consists of |
1050 | @@ -394,8 +368,8 @@ |
1051 | std::function<std::list<std::pair<std::string, std::string>>(void)> envfunc = [this, instance]() { |
1052 | return launchEnv(instance); |
1053 | }; |
1054 | - return UpstartInstance::launch(appId(), "application-legacy", instance, urls, _registry, |
1055 | - UpstartInstance::launchMode::STANDARD, envfunc); |
1056 | + return _registry->impl->jobs->launch(appId(), "application-legacy", instance, urls, |
1057 | + jobs::manager::launchMode::STANDARD, envfunc); |
1058 | } |
1059 | |
1060 | /** Create an UpstartInstance for this AppID using the UpstartInstance launch |
1061 | @@ -409,8 +383,8 @@ |
1062 | std::function<std::list<std::pair<std::string, std::string>>(void)> envfunc = [this, instance]() { |
1063 | return launchEnv(instance); |
1064 | }; |
1065 | - return UpstartInstance::launch(appId(), "application-legacy", instance, urls, _registry, |
1066 | - UpstartInstance::launchMode::TEST, envfunc); |
1067 | + return _registry->impl->jobs->launch(appId(), "application-legacy", instance, urls, jobs::manager::launchMode::TEST, |
1068 | + envfunc); |
1069 | } |
1070 | |
1071 | } // namespace app_impls |
1072 | |
1073 | === modified file 'libubuntu-app-launch/application-impl-libertine.cpp' |
1074 | --- libubuntu-app-launch/application-impl-libertine.cpp 2016-10-03 23:54:20 +0000 |
1075 | +++ libubuntu-app-launch/application-impl-libertine.cpp 2016-11-10 21:58:30 +0000 |
1076 | @@ -255,8 +255,7 @@ |
1077 | } |
1078 | catch (std::runtime_error& e) |
1079 | { |
1080 | - g_debug("Unable to create application for libertine appname '%s': %s", |
1081 | - apps.get()[j], e.what()); |
1082 | + g_debug("Unable to create application for libertine appname '%s': %s", apps.get()[j], e.what()); |
1083 | } |
1084 | } |
1085 | } |
1086 | @@ -276,17 +275,8 @@ |
1087 | |
1088 | std::vector<std::shared_ptr<Application::Instance>> Libertine::instances() |
1089 | { |
1090 | - std::vector<std::shared_ptr<Instance>> vect; |
1091 | - std::string sappid = appId(); |
1092 | - |
1093 | - for (auto instancename : _registry->impl->upstartInstancesForJob("application-legacy")) |
1094 | - { |
1095 | - if (std::equal(sappid.begin(), sappid.end(), instancename.begin())) |
1096 | - vect.emplace_back(std::make_shared<UpstartInstance>(appId(), "application-legacy", std::string{}, |
1097 | - std::vector<Application::URL>{}, _registry)); |
1098 | - } |
1099 | - |
1100 | - return vect; |
1101 | + auto vbase = _registry->impl->jobs->instances(appId(), "application-legacy"); |
1102 | + return std::vector<std::shared_ptr<Application::Instance>>(vbase.begin(), vbase.end()); |
1103 | } |
1104 | |
1105 | /** Grabs all the environment variables for the application to |
1106 | @@ -328,15 +318,15 @@ |
1107 | std::shared_ptr<Application::Instance> Libertine::launch(const std::vector<Application::URL>& urls) |
1108 | { |
1109 | std::function<std::list<std::pair<std::string, std::string>>(void)> envfunc = [this]() { return launchEnv(); }; |
1110 | - return UpstartInstance::launch(appId(), "application-legacy", {}, urls, _registry, |
1111 | - UpstartInstance::launchMode::STANDARD, envfunc); |
1112 | + return _registry->impl->jobs->launch(appId(), "application-legacy", {}, urls, jobs::manager::launchMode::STANDARD, |
1113 | + envfunc); |
1114 | } |
1115 | |
1116 | std::shared_ptr<Application::Instance> Libertine::launchTest(const std::vector<Application::URL>& urls) |
1117 | { |
1118 | std::function<std::list<std::pair<std::string, std::string>>(void)> envfunc = [this]() { return launchEnv(); }; |
1119 | - return UpstartInstance::launch(appId(), "application-legacy", {}, urls, _registry, |
1120 | - UpstartInstance::launchMode::TEST, envfunc); |
1121 | + return _registry->impl->jobs->launch(appId(), "application-legacy", {}, urls, jobs::manager::launchMode::TEST, |
1122 | + envfunc); |
1123 | } |
1124 | |
1125 | } // namespace app_impls |
1126 | |
1127 | === modified file 'libubuntu-app-launch/application-impl-snap.cpp' |
1128 | --- libubuntu-app-launch/application-impl-snap.cpp 2016-10-03 23:54:20 +0000 |
1129 | +++ libubuntu-app-launch/application-impl-snap.cpp 2016-11-10 21:58:30 +0000 |
1130 | @@ -396,19 +396,8 @@ |
1131 | /** Get all of the instances of this snap package that are running */ |
1132 | std::vector<std::shared_ptr<Application::Instance>> Snap::instances() |
1133 | { |
1134 | - std::vector<std::shared_ptr<Instance>> vect; |
1135 | - auto startsWith = std::string(appid_) + "-"; |
1136 | - |
1137 | - for (const auto& instance : _registry->impl->upstartInstancesForJob("application-snap")) |
1138 | - { |
1139 | - if (std::equal(startsWith.begin(), startsWith.end(), instance.begin())) |
1140 | - { |
1141 | - vect.emplace_back(std::make_shared<UpstartInstance>(appid_, "application-snap", std::string{}, |
1142 | - std::vector<Application::URL>{}, _registry)); |
1143 | - } |
1144 | - } |
1145 | - |
1146 | - return vect; |
1147 | + auto vbase = _registry->impl->jobs->instances(appId(), "application-snap"); |
1148 | + return std::vector<std::shared_ptr<Application::Instance>>(vbase.begin(), vbase.end()); |
1149 | } |
1150 | |
1151 | /** Return the launch environment for this snap. That includes whether |
1152 | @@ -448,8 +437,8 @@ |
1153 | std::shared_ptr<Application::Instance> Snap::launch(const std::vector<Application::URL>& urls) |
1154 | { |
1155 | std::function<std::list<std::pair<std::string, std::string>>(void)> envfunc = [this]() { return launchEnv(); }; |
1156 | - return UpstartInstance::launch(appid_, "application-snap", {}, urls, _registry, |
1157 | - UpstartInstance::launchMode::STANDARD, envfunc); |
1158 | + return _registry->impl->jobs->launch(appid_, "application-snap", {}, urls, jobs::manager::launchMode::STANDARD, |
1159 | + envfunc); |
1160 | } |
1161 | |
1162 | /** Create a new instance of this Snap with a testing environment |
1163 | @@ -460,8 +449,8 @@ |
1164 | std::shared_ptr<Application::Instance> Snap::launchTest(const std::vector<Application::URL>& urls) |
1165 | { |
1166 | std::function<std::list<std::pair<std::string, std::string>>(void)> envfunc = [this]() { return launchEnv(); }; |
1167 | - return UpstartInstance::launch(appid_, "application-snap", {}, urls, _registry, UpstartInstance::launchMode::TEST, |
1168 | - envfunc); |
1169 | + return _registry->impl->jobs->launch(appid_, "application-snap", {}, urls, jobs::manager::launchMode::TEST, |
1170 | + envfunc); |
1171 | } |
1172 | |
1173 | } // namespace app_impls |
1174 | |
1175 | === modified file 'libubuntu-app-launch/application.cpp' |
1176 | --- libubuntu-app-launch/application.cpp 2016-08-26 17:33:34 +0000 |
1177 | +++ libubuntu-app-launch/application.cpp 2016-11-10 21:58:30 +0000 |
1178 | @@ -28,6 +28,8 @@ |
1179 | #include "application-impl-snap.h" |
1180 | #endif |
1181 | #include "application.h" |
1182 | +#include "jobs-base.h" |
1183 | +#include "registry-impl.h" |
1184 | #include "registry.h" |
1185 | |
1186 | #include <functional> |
1187 | @@ -46,6 +48,11 @@ |
1188 | throw std::runtime_error("AppID is empty"); |
1189 | } |
1190 | |
1191 | + if (!registry->impl->jobs) |
1192 | + { |
1193 | + registry->impl->jobs = jobs::manager::Base::determineFactory(registry); |
1194 | + } |
1195 | + |
1196 | if (app_impls::Click::hasAppId(appid, registry)) |
1197 | { |
1198 | return std::make_shared<app_impls::Click>(appid, registry); |
1199 | |
1200 | === added file 'libubuntu-app-launch/jobs-base.cpp' |
1201 | --- libubuntu-app-launch/jobs-base.cpp 1970-01-01 00:00:00 +0000 |
1202 | +++ libubuntu-app-launch/jobs-base.cpp 2016-11-10 21:58:30 +0000 |
1203 | @@ -0,0 +1,392 @@ |
1204 | +/* |
1205 | + * Copyright © 2016 Canonical Ltd. |
1206 | + * |
1207 | + * This program is free software: you can redistribute it and/or modify it |
1208 | + * under the terms of the GNU General Public License version 3, as published |
1209 | + * by the Free Software Foundation. |
1210 | + * |
1211 | + * This program is distributed in the hope that it will be useful, but |
1212 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
1213 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1214 | + * PURPOSE. See the GNU General Public License for more details. |
1215 | + * |
1216 | + * You should have received a copy of the GNU General Public License along |
1217 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
1218 | + * |
1219 | + * Authors: |
1220 | + * Ted Gould <ted.gould@canonical.com> |
1221 | + */ |
1222 | + |
1223 | +#include <algorithm> |
1224 | +#include <cerrno> |
1225 | +#include <cstring> |
1226 | +#include <numeric> |
1227 | + |
1228 | +#include "jobs-base.h" |
1229 | +#include "jobs-upstart.h" |
1230 | +#include "registry-impl.h" |
1231 | + |
1232 | +namespace ubuntu |
1233 | +{ |
1234 | +namespace app_launch |
1235 | +{ |
1236 | +namespace jobs |
1237 | +{ |
1238 | +namespace manager |
1239 | +{ |
1240 | + |
1241 | +Base::Base(const std::shared_ptr<Registry>& registry) |
1242 | + : registry_(registry) |
1243 | +{ |
1244 | +} |
1245 | + |
1246 | +std::shared_ptr<Base> Base::determineFactory(std::shared_ptr<Registry> registry) |
1247 | +{ |
1248 | + return std::make_shared<jobs::manager::Upstart>(registry); |
1249 | +} |
1250 | + |
1251 | +} // namespace manager |
1252 | + |
1253 | +namespace instance |
1254 | +{ |
1255 | + |
1256 | +Base::Base(const AppID& appId, |
1257 | + const std::string& job, |
1258 | + const std::string& instance, |
1259 | + const std::vector<Application::URL>& urls, |
1260 | + const std::shared_ptr<Registry>& registry) |
1261 | + : appId_(appId) |
1262 | + , job_(job) |
1263 | + , instance_(instance) |
1264 | + , urls_(urls) |
1265 | + , registry_(registry) |
1266 | +{ |
1267 | +} |
1268 | + |
1269 | +/** Checks to see if we have a primary PID for the instance */ |
1270 | +bool Base::isRunning() |
1271 | +{ |
1272 | + return primaryPid() != 0; |
1273 | +} |
1274 | + |
1275 | +/** Looks at the PIDs in the instance cgroup and checks to see if @pid |
1276 | + is in the set. |
1277 | + |
1278 | + @param pid PID to look for |
1279 | +*/ |
1280 | +bool Base::hasPid(pid_t pid) |
1281 | +{ |
1282 | + auto vpids = pids(); |
1283 | + return std::find(vpids.begin(), vpids.end(), pid) != vpids.end(); |
1284 | +} |
1285 | + |
1286 | +/** Pauses this application by sending SIGSTOP to all the PIDs in the |
1287 | + cgroup and tells Zeitgeist that we've left the application. */ |
1288 | +void Base::pause() |
1289 | +{ |
1290 | + g_debug("Pausing application: %s", std::string(appId_).c_str()); |
1291 | + registry_->impl->zgSendEvent(appId_, ZEITGEIST_ZG_LEAVE_EVENT); |
1292 | + |
1293 | + auto pids = forAllPids([this](pid_t pid) { |
1294 | + auto oomval = oom::paused(); |
1295 | + g_debug("Pausing PID: %d (%d)", pid, int(oomval)); |
1296 | + signalToPid(pid, SIGSTOP); |
1297 | + oomValueToPid(pid, oomval); |
1298 | + }); |
1299 | + |
1300 | + pidListToDbus(pids, "ApplicationPaused"); |
1301 | +} |
1302 | + |
1303 | +/** Resumes this application by sending SIGCONT to all the PIDs in the |
1304 | + cgroup and tells Zeitgeist that we're accessing the application. */ |
1305 | +void Base::resume() |
1306 | +{ |
1307 | + g_debug("Resuming application: %s", std::string(appId_).c_str()); |
1308 | + registry_->impl->zgSendEvent(appId_, ZEITGEIST_ZG_ACCESS_EVENT); |
1309 | + |
1310 | + auto pids = forAllPids([this](pid_t pid) { |
1311 | + auto oomval = oom::focused(); |
1312 | + g_debug("Resuming PID: %d (%d)", pid, int(oomval)); |
1313 | + signalToPid(pid, SIGCONT); |
1314 | + oomValueToPid(pid, oomval); |
1315 | + }); |
1316 | + |
1317 | + pidListToDbus(pids, "ApplicationResumed"); |
1318 | +} |
1319 | + |
1320 | +/** Go through the list of PIDs calling a function and handling |
1321 | + the issue with getting PIDs being a racey condition. |
1322 | + |
1323 | + \param eachPid Function to run on each PID |
1324 | +*/ |
1325 | +std::vector<pid_t> Base::forAllPids(std::function<void(pid_t)> eachPid) |
1326 | +{ |
1327 | + std::set<pid_t> seenPids; |
1328 | + bool added = true; |
1329 | + |
1330 | + while (added) |
1331 | + { |
1332 | + added = false; |
1333 | + auto pidlist = pids(); |
1334 | + for (auto pid : pidlist) |
1335 | + { |
1336 | + if (seenPids.insert(pid).second) |
1337 | + { |
1338 | + eachPid(pid); |
1339 | + added = true; |
1340 | + } |
1341 | + } |
1342 | + } |
1343 | + |
1344 | + return std::vector<pid_t>(seenPids.begin(), seenPids.end()); |
1345 | +} |
1346 | + |
1347 | +/** Send a signal that we've change the application. Do this on the |
1348 | + registry thread in an idle so that we don't block anyone. |
1349 | + |
1350 | + \param pids List of PIDs to turn into variants to send |
1351 | + \param signal Name of the DBus signal to send |
1352 | +*/ |
1353 | +void Base::pidListToDbus(const std::vector<pid_t>& pids, const std::string& signal) |
1354 | +{ |
1355 | + auto registry = registry_; |
1356 | + auto lappid = appId_; |
1357 | + |
1358 | + registry_->impl->thread.executeOnThread([registry, lappid, pids, signal] { |
1359 | + auto vpids = std::shared_ptr<GVariant>( |
1360 | + [pids]() { |
1361 | + GVariant* pidarray = nullptr; |
1362 | + |
1363 | + if (pids.empty()) |
1364 | + { |
1365 | + pidarray = g_variant_new_array(G_VARIANT_TYPE_UINT64, nullptr, 0); |
1366 | + g_variant_ref_sink(pidarray); |
1367 | + return pidarray; |
1368 | + } |
1369 | + |
1370 | + GVariantBuilder builder; |
1371 | + g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY); |
1372 | + for (auto pid : pids) |
1373 | + { |
1374 | + g_variant_builder_add_value(&builder, g_variant_new_uint64(pid)); |
1375 | + } |
1376 | + |
1377 | + pidarray = g_variant_builder_end(&builder); |
1378 | + g_variant_ref_sink(pidarray); |
1379 | + return pidarray; |
1380 | + }(), |
1381 | + [](GVariant* var) { g_variant_unref(var); }); |
1382 | + |
1383 | + GVariantBuilder params; |
1384 | + g_variant_builder_init(¶ms, G_VARIANT_TYPE_TUPLE); |
1385 | + g_variant_builder_add_value(¶ms, g_variant_new_string(std::string(lappid).c_str())); |
1386 | + g_variant_builder_add_value(¶ms, vpids.get()); |
1387 | + |
1388 | + GError* error = nullptr; |
1389 | + g_dbus_connection_emit_signal(registry->impl->_dbus.get(), /* bus */ |
1390 | + nullptr, /* destination */ |
1391 | + "/", /* path */ |
1392 | + "com.canonical.UbuntuAppLaunch", /* interface */ |
1393 | + signal.c_str(), /* signal */ |
1394 | + g_variant_builder_end(¶ms), /* params, the same */ |
1395 | + &error); /* error */ |
1396 | + |
1397 | + if (error != nullptr) |
1398 | + { |
1399 | + g_warning("Unable to emit signal '%s' for appid '%s': %s", signal.c_str(), std::string(lappid).c_str(), |
1400 | + error->message); |
1401 | + g_error_free(error); |
1402 | + } |
1403 | + else |
1404 | + { |
1405 | + g_debug("Emmitted '%s' to DBus", signal.c_str()); |
1406 | + } |
1407 | + }); |
1408 | +} |
1409 | + |
1410 | +/** Sets the OOM adjustment by getting the list of PIDs and writing |
1411 | + the value to each of their files in proc |
1412 | + |
1413 | + \param score OOM Score to set |
1414 | +*/ |
1415 | +void Base::setOomAdjustment(const oom::Score score) |
1416 | +{ |
1417 | + forAllPids([this, &score](pid_t pid) { oomValueToPid(pid, score); }); |
1418 | +} |
1419 | + |
1420 | +/** Figures out the path to the primary PID of the application and |
1421 | + then reads its OOM adjustment file. */ |
1422 | +const oom::Score Base::getOomAdjustment() |
1423 | +{ |
1424 | + auto pid = primaryPid(); |
1425 | + if (pid == 0) |
1426 | + { |
1427 | + throw std::runtime_error("No PID for application: " + std::string(appId_)); |
1428 | + } |
1429 | + |
1430 | + auto path = pidToOomPath(pid); |
1431 | + GError* error = nullptr; |
1432 | + gchar* content = nullptr; |
1433 | + |
1434 | + g_file_get_contents(path.c_str(), /* path */ |
1435 | + &content, /* data */ |
1436 | + nullptr, /* size */ |
1437 | + &error); /* error */ |
1438 | + |
1439 | + if (error != nullptr) |
1440 | + { |
1441 | + auto serror = std::shared_ptr<GError>(error, g_error_free); |
1442 | + throw std::runtime_error("Unable to access OOM value for '" + std::string(appId_) + "' primary PID '" + |
1443 | + std::to_string(pid) + "' because: " + serror->message); |
1444 | + } |
1445 | + |
1446 | + auto score = static_cast<oom::Score>(std::atoi(content)); |
1447 | + g_free(content); |
1448 | + return score; |
1449 | +} |
1450 | + |
1451 | +/** Sends a signal to a PID with a warning if we can't send it. |
1452 | + We could throw an exception, but we can't handle it usefully anyway |
1453 | + |
1454 | + \param pid PID to send the signal to |
1455 | + \param signal signal to send |
1456 | +*/ |
1457 | +void Base::signalToPid(pid_t pid, int signal) |
1458 | +{ |
1459 | + if (-1 == kill(pid, signal)) |
1460 | + { |
1461 | + /* While that didn't work, we still want to try as many as we can */ |
1462 | + g_warning("Unable to send signal %d to pid %d", signal, pid); |
1463 | + } |
1464 | +} |
1465 | + |
1466 | +/** Get the path to the PID's OOM adjust path, with allowing for an |
1467 | + override for testing using the environment variable |
1468 | + UBUNTU_APP_LAUNCH_OOM_PROC_PATH |
1469 | + |
1470 | + \param pid PID to build path for |
1471 | +*/ |
1472 | +std::string Base::pidToOomPath(pid_t pid) |
1473 | +{ |
1474 | + static std::string procpath; |
1475 | + if (G_UNLIKELY(procpath.empty())) |
1476 | + { |
1477 | + /* Set by the test suite, probably not anyone else */ |
1478 | + auto envvar = g_getenv("UBUNTU_APP_LAUNCH_OOM_PROC_PATH"); |
1479 | + if (G_LIKELY(envvar == nullptr)) |
1480 | + procpath = "/proc"; |
1481 | + else |
1482 | + procpath = envvar; |
1483 | + } |
1484 | + |
1485 | + gchar* gpath = g_build_filename(procpath.c_str(), std::to_string(pid).c_str(), "oom_score_adj", nullptr); |
1486 | + std::string path = gpath; |
1487 | + g_free(gpath); |
1488 | + return path; |
1489 | +} |
1490 | + |
1491 | +/** Writes an OOM value to proc, assuming we have a string |
1492 | + in the outer loop |
1493 | + |
1494 | + \param pid PID to change the OOM value of |
1495 | + \param oomvalue OOM value to set |
1496 | +*/ |
1497 | +void Base::oomValueToPid(pid_t pid, const oom::Score oomvalue) |
1498 | +{ |
1499 | + auto oomstr = std::to_string(static_cast<std::int32_t>(oomvalue)); |
1500 | + auto path = pidToOomPath(pid); |
1501 | + FILE* adj = fopen(path.c_str(), "w"); |
1502 | + int openerr = errno; |
1503 | + |
1504 | + if (adj == nullptr) |
1505 | + { |
1506 | + switch (openerr) |
1507 | + { |
1508 | + case ENOENT: |
1509 | + /* ENOENT happens a fair amount because of races, so it's not |
1510 | + worth printing a warning about */ |
1511 | + return; |
1512 | + case EACCES: |
1513 | + { |
1514 | + /* We can get this error when trying to set the OOM value on |
1515 | + Oxide renderers because they're started by the sandbox and |
1516 | + don't have their adjustment value available for us to write. |
1517 | + We have a helper to deal with this, but it's kinda expensive |
1518 | + so we only use it when we have to. */ |
1519 | + oomValueToPidHelper(pid, oomvalue); |
1520 | + return; |
1521 | + } |
1522 | + default: |
1523 | + g_warning("Unable to set OOM value for '%d' to '%s': %s", int(pid), oomstr.c_str(), |
1524 | + std::strerror(openerr)); |
1525 | + return; |
1526 | + } |
1527 | + } |
1528 | + |
1529 | + size_t writesize = fwrite(oomstr.c_str(), 1, oomstr.size(), adj); |
1530 | + int writeerr = errno; |
1531 | + fclose(adj); |
1532 | + |
1533 | + if (writesize == oomstr.size()) |
1534 | + return; |
1535 | + |
1536 | + if (writeerr != 0) |
1537 | + g_warning("Unable to set OOM value for '%d' to '%s': %s", int(pid), oomstr.c_str(), strerror(writeerr)); |
1538 | + else |
1539 | + /* No error, but yet, wrong size. Not sure, what could cause this. */ |
1540 | + g_debug("Unable to set OOM value for '%d' to '%s': Wrote %d bytes", int(pid), oomstr.c_str(), int(writesize)); |
1541 | +} |
1542 | + |
1543 | +/** Use a setuid root helper for setting the oom value of |
1544 | + Chromium instances |
1545 | + |
1546 | + \param pid PID to change the OOM value of |
1547 | + \param oomvalue OOM value to set |
1548 | +*/ |
1549 | +void Base::oomValueToPidHelper(pid_t pid, const oom::Score oomvalue) |
1550 | +{ |
1551 | + GError* error = nullptr; |
1552 | + std::string oomstr = std::to_string(static_cast<std::int32_t>(oomvalue)); |
1553 | + std::string pidstr = std::to_string(pid); |
1554 | + std::array<const char*, 4> args = {OOM_HELPER, pidstr.c_str(), oomstr.c_str(), nullptr}; |
1555 | + |
1556 | + g_debug("Excuting OOM Helper (pid: %d, score: %d): %s", int(pid), int(oomvalue), |
1557 | + std::accumulate(args.begin(), args.end(), std::string{}, |
1558 | + [](const std::string& instr, const char* output) -> std::string { |
1559 | + if (instr.empty()) |
1560 | + { |
1561 | + return output; |
1562 | + } |
1563 | + else if (output != nullptr) |
1564 | + { |
1565 | + return instr + " " + std::string(output); |
1566 | + } |
1567 | + else |
1568 | + { |
1569 | + return instr; |
1570 | + } |
1571 | + }) |
1572 | + .c_str()); |
1573 | + |
1574 | + g_spawn_async(nullptr, /* working dir */ |
1575 | + (char**)(args.data()), /* args */ |
1576 | + nullptr, /* env */ |
1577 | + G_SPAWN_DEFAULT, /* flags */ |
1578 | + nullptr, /* child setup */ |
1579 | + nullptr, /* child setup userdata*/ |
1580 | + nullptr, /* pid */ |
1581 | + &error); /* error */ |
1582 | + |
1583 | + if (error != nullptr) |
1584 | + { |
1585 | + g_warning("Unable to launch OOM helper '" OOM_HELPER "' on PID '%d': %s", pid, error->message); |
1586 | + g_error_free(error); |
1587 | + return; |
1588 | + } |
1589 | +} |
1590 | + |
1591 | +} // namespace instance |
1592 | + |
1593 | +} // namespace jobs |
1594 | +} // namespace app_launch |
1595 | +} // namespace ubuntu |
1596 | |
1597 | === added file 'libubuntu-app-launch/jobs-base.h' |
1598 | --- libubuntu-app-launch/jobs-base.h 1970-01-01 00:00:00 +0000 |
1599 | +++ libubuntu-app-launch/jobs-base.h 2016-11-10 21:58:30 +0000 |
1600 | @@ -0,0 +1,117 @@ |
1601 | +/* |
1602 | + * Copyright © 2016 Canonical Ltd. |
1603 | + * |
1604 | + * This program is free software: you can redistribute it and/or modify it |
1605 | + * under the terms of the GNU General Public License version 3, as published |
1606 | + * by the Free Software Foundation. |
1607 | + * |
1608 | + * This program is distributed in the hope that it will be useful, but |
1609 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
1610 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1611 | + * PURPOSE. See the GNU General Public License for more details. |
1612 | + * |
1613 | + * You should have received a copy of the GNU General Public License along |
1614 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
1615 | + * |
1616 | + * Authors: |
1617 | + * Ted Gould <ted.gould@canonical.com> |
1618 | + */ |
1619 | + |
1620 | +#pragma once |
1621 | +#include "application.h" |
1622 | + |
1623 | +namespace ubuntu |
1624 | +{ |
1625 | +namespace app_launch |
1626 | +{ |
1627 | +namespace jobs |
1628 | +{ |
1629 | +namespace instance |
1630 | +{ |
1631 | + |
1632 | +class Base : public Application::Instance |
1633 | +{ |
1634 | +public: |
1635 | + Base(const AppID& appId, |
1636 | + const std::string& job, |
1637 | + const std::string& instance, |
1638 | + const std::vector<Application::URL>& urls, |
1639 | + const std::shared_ptr<Registry>& registry); |
1640 | + virtual ~Base() = default; |
1641 | + |
1642 | + bool isRunning() override; |
1643 | + bool hasPid(pid_t pid) override; |
1644 | + void pause() override; |
1645 | + void resume() override; |
1646 | + |
1647 | + /* OOM Functions */ |
1648 | + void setOomAdjustment(const oom::Score score) override; |
1649 | + const oom::Score getOomAdjustment() override; |
1650 | + |
1651 | +protected: |
1652 | + /** Application ID */ |
1653 | + const AppID appId_; |
1654 | + /** Upstart job name */ |
1655 | + const std::string job_; |
1656 | + /** Instance ID environment value, empty if none */ |
1657 | + const std::string instance_; |
1658 | + /** The URLs that this was launched for. Only valid on launched jobs, we |
1659 | + should look at perhaps changing that. */ |
1660 | + std::vector<Application::URL> urls_; |
1661 | + /** A link to the registry we're using for connections */ |
1662 | + std::shared_ptr<Registry> registry_; |
1663 | + |
1664 | +private: |
1665 | + std::vector<pid_t> forAllPids(std::function<void(pid_t)> eachPid); |
1666 | + void signalToPid(pid_t pid, int signal); |
1667 | + std::string pidToOomPath(pid_t pid); |
1668 | + void oomValueToPid(pid_t pid, const oom::Score oomvalue); |
1669 | + void oomValueToPidHelper(pid_t pid, const oom::Score oomvalue); |
1670 | + void pidListToDbus(const std::vector<pid_t>& pids, const std::string& signal); |
1671 | +}; |
1672 | + |
1673 | +} // namespace instance |
1674 | + |
1675 | +namespace manager |
1676 | +{ |
1677 | + |
1678 | +/** Flag for whether we should include the testing environment variables */ |
1679 | +enum class launchMode |
1680 | +{ |
1681 | + STANDARD, /**< Standard variable set */ |
1682 | + TEST /**< Include testing environment vars */ |
1683 | +}; |
1684 | + |
1685 | +class Base |
1686 | +{ |
1687 | +public: |
1688 | + Base(const std::shared_ptr<Registry>& registry); |
1689 | + virtual ~Base() = default; |
1690 | + |
1691 | + virtual std::shared_ptr<Application::Instance> launch( |
1692 | + const AppID& appId, |
1693 | + const std::string& job, |
1694 | + const std::string& instance, |
1695 | + const std::vector<Application::URL>& urls, |
1696 | + launchMode mode, |
1697 | + std::function<std::list<std::pair<std::string, std::string>>(void)>& getenv) = 0; |
1698 | + |
1699 | + virtual std::shared_ptr<Application::Instance> existing(const AppID& appId, |
1700 | + const std::string& job, |
1701 | + const std::string& instance, |
1702 | + const std::vector<Application::URL>& urls) = 0; |
1703 | + |
1704 | + virtual std::list<std::shared_ptr<Application>> runningApps() = 0; |
1705 | + |
1706 | + virtual std::vector<std::shared_ptr<instance::Base>> instances(const AppID& appID, const std::string& job) = 0; |
1707 | + |
1708 | + static std::shared_ptr<Base> determineFactory(std::shared_ptr<Registry> registry); |
1709 | + |
1710 | +protected: |
1711 | + std::weak_ptr<Registry> registry_; |
1712 | +}; |
1713 | + |
1714 | +} // namespace manager |
1715 | +} // namespace jobs |
1716 | +} // namespace app_launch |
1717 | +} // namespace ubuntu |
1718 | |
1719 | === added file 'libubuntu-app-launch/jobs-upstart.cpp' |
1720 | --- libubuntu-app-launch/jobs-upstart.cpp 1970-01-01 00:00:00 +0000 |
1721 | +++ libubuntu-app-launch/jobs-upstart.cpp 2016-11-10 21:58:30 +0000 |
1722 | @@ -0,0 +1,899 @@ |
1723 | +/* |
1724 | + * Copyright © 2016 Canonical Ltd. |
1725 | + * |
1726 | + * This program is free software: you can redistribute it and/or modify it |
1727 | + * under the terms of the GNU General Public License version 3, as published |
1728 | + * by the Free Software Foundation. |
1729 | + * |
1730 | + * This program is distributed in the hope that it will be useful, but |
1731 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
1732 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
1733 | + * PURPOSE. See the GNU General Public License for more details. |
1734 | + * |
1735 | + * You should have received a copy of the GNU General Public License along |
1736 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
1737 | + * |
1738 | + * Authors: |
1739 | + * Ted Gould <ted.gould@canonical.com> |
1740 | + */ |
1741 | + |
1742 | +#include <algorithm> |
1743 | +#include <cerrno> |
1744 | +#include <cstring> |
1745 | +#include <map> |
1746 | +#include <numeric> |
1747 | +#include <regex> |
1748 | + |
1749 | +#include <cgmanager/cgmanager.h> |
1750 | +#include <upstart.h> |
1751 | + |
1752 | +#include "helpers.h" |
1753 | +#include "registry-impl.h" |
1754 | +#include "second-exec-core.h" |
1755 | + |
1756 | +extern "C" { |
1757 | +#include "ubuntu-app-launch-trace.h" |
1758 | +} |
1759 | + |
1760 | +#include "jobs-upstart.h" |
1761 | + |
1762 | +namespace ubuntu |
1763 | +{ |
1764 | +namespace app_launch |
1765 | +{ |
1766 | +namespace jobs |
1767 | +{ |
1768 | +namespace instance |
1769 | +{ |
1770 | + |
1771 | +/** An object that represents an instance of a job on Upstart. This |
1772 | + then implements everything needed by the instance interface. Most |
1773 | + applications tie into this today and use it as the backend for |
1774 | + their instances. */ |
1775 | +class Upstart : public Base |
1776 | +{ |
1777 | +public: |
1778 | + explicit Upstart(const AppID& appId, |
1779 | + const std::string& job, |
1780 | + const std::string& instance, |
1781 | + const std::vector<Application::URL>& urls, |
1782 | + const std::shared_ptr<Registry>& registry); |
1783 | + |
1784 | + /* Query lifecycle */ |
1785 | + pid_t primaryPid() override; |
1786 | + std::string logPath() override; |
1787 | + std::vector<pid_t> pids() override; |
1788 | + |
1789 | + /* Manage lifecycle */ |
1790 | + void stop() override; |
1791 | + |
1792 | + /* C Callback */ |
1793 | + static void application_start_cb(GObject* obj, GAsyncResult* res, gpointer user_data); |
1794 | + |
1795 | +private: |
1796 | + std::string upstartJobPath(const std::string& job); |
1797 | + std::string upstartName(); |
1798 | + |
1799 | + static std::shared_ptr<gchar*> urlsToStrv(const std::vector<Application::URL>& urls); |
1800 | +}; |
1801 | + |
1802 | +/** Uses Upstart to get the primary PID of the instance using Upstart's |
1803 | + DBus interface */ |
1804 | +pid_t Upstart::primaryPid() |
1805 | +{ |
1806 | + auto jobpath = upstartJobPath(job_); |
1807 | + if (jobpath.empty()) |
1808 | + { |
1809 | + g_debug("Unable to get a valid job path"); |
1810 | + return 0; |
1811 | + } |
1812 | + |
1813 | + return registry_->impl->thread.executeOnThread<pid_t>([this, &jobpath]() -> pid_t { |
1814 | + GError* error = nullptr; |
1815 | + |
1816 | + std::string instancename = std::string(appId_); |
1817 | + if (job_ != "application-click") |
1818 | + { |
1819 | + instancename += "-" + instance_; |
1820 | + } |
1821 | + |
1822 | + g_debug("Getting instance by name: %s", instance_.c_str()); |
1823 | + GVariant* vinstance_path = |
1824 | + g_dbus_connection_call_sync(registry_->impl->_dbus.get(), /* connection */ |
1825 | + DBUS_SERVICE_UPSTART, /* service */ |
1826 | + jobpath.c_str(), /* object path */ |
1827 | + DBUS_INTERFACE_UPSTART_JOB, /* iface */ |
1828 | + "GetInstanceByName", /* method */ |
1829 | + g_variant_new("(s)", instancename.c_str()), /* params */ |
1830 | + G_VARIANT_TYPE("(o)"), /* return type */ |
1831 | + G_DBUS_CALL_FLAGS_NONE, /* flags */ |
1832 | + -1, /* timeout: default */ |
1833 | + registry_->impl->thread.getCancellable().get(), /* cancellable */ |
1834 | + &error); |
1835 | + |
1836 | + if (error != nullptr) |
1837 | + { |
1838 | + g_warning("Unable to get instance '%s' of job '%s': %s", instance_.c_str(), job_.c_str(), error->message); |
1839 | + g_error_free(error); |
1840 | + return 0; |
1841 | + } |
1842 | + |
1843 | + /* Jump rope to make this into a C++ type */ |
1844 | + std::string instance_path; |
1845 | + gchar* cinstance_path = nullptr; |
1846 | + g_variant_get(vinstance_path, "(o)", &cinstance_path); |
1847 | + g_variant_unref(vinstance_path); |
1848 | + if (cinstance_path != nullptr) |
1849 | + { |
1850 | + instance_path = cinstance_path; |
1851 | + g_free(cinstance_path); |
1852 | + } |
1853 | + |
1854 | + if (instance_path.empty()) |
1855 | + { |
1856 | + g_debug("No instance object for instance name: %s", instance_.c_str()); |
1857 | + return 0; |
1858 | + } |
1859 | + |
1860 | + GVariant* props_tuple = |
1861 | + g_dbus_connection_call_sync(registry_->impl->_dbus.get(), /* connection */ |
1862 | + DBUS_SERVICE_UPSTART, /* service */ |
1863 | + instance_path.c_str(), /* object path */ |
1864 | + "org.freedesktop.DBus.Properties", /* interface */ |
1865 | + "GetAll", /* method */ |
1866 | + g_variant_new("(s)", DBUS_INTERFACE_UPSTART_INSTANCE), /* params */ |
1867 | + G_VARIANT_TYPE("(a{sv})"), /* return type */ |
1868 | + G_DBUS_CALL_FLAGS_NONE, /* flags */ |
1869 | + -1, /* timeout: default */ |
1870 | + registry_->impl->thread.getCancellable().get(), /* cancellable */ |
1871 | + &error); |
1872 | + |
1873 | + if (error != nullptr) |
1874 | + { |
1875 | + g_warning("Unable to name of properties '%s': %s", instance_path.c_str(), error->message); |
1876 | + g_error_free(error); |
1877 | + error = nullptr; |
1878 | + return 0; |
1879 | + } |
1880 | + |
1881 | + GVariant* props_dict = g_variant_get_child_value(props_tuple, 0); |
1882 | + |
1883 | + pid_t retval = 0; |
1884 | + GVariant* processes = g_variant_lookup_value(props_dict, "processes", G_VARIANT_TYPE("a(si)")); |
1885 | + if (processes != nullptr && g_variant_n_children(processes) > 0) |
1886 | + { |
1887 | + |
1888 | + GVariant* first_entry = g_variant_get_child_value(processes, 0); |
1889 | + GVariant* pidv = g_variant_get_child_value(first_entry, 1); |
1890 | + |
1891 | + retval = g_variant_get_int32(pidv); |
1892 | + |
1893 | + g_variant_unref(pidv); |
1894 | + g_variant_unref(first_entry); |
1895 | + } |
1896 | + else |
1897 | + { |
1898 | + g_debug("Unable to get 'processes' from properties of instance at path: %s", instance_path.c_str()); |
1899 | + } |
1900 | + |
1901 | + g_variant_unref(props_dict); |
1902 | + |
1903 | + return retval; |
1904 | + }); |
1905 | +} |
1906 | + |
1907 | +/** Generate the full name of the Upstart job for the job, the |
1908 | + instance and how all those fit together. |
1909 | + |
1910 | + Handles the special case of application-click which isn't designed |
1911 | + to have multi-instance apps. |
1912 | +*/ |
1913 | +std::string Upstart::upstartName() |
1914 | +{ |
1915 | + std::string path = job_ + "-" + std::string(appId_); |
1916 | + if (job_ != "application-click") |
1917 | + { |
1918 | + path += "-"; |
1919 | + } |
1920 | + if (!instance_.empty()) |
1921 | + { |
1922 | + path += instance_; |
1923 | + } |
1924 | + |
1925 | + return path; |
1926 | +} |
1927 | + |
1928 | +/** Gets the path to the log file for this instance */ |
1929 | +std::string Upstart::logPath() |
1930 | +{ |
1931 | + std::string logfile = upstartName() + ".log"; |
1932 | + |
1933 | + gchar* cpath = g_build_filename(g_get_user_cache_dir(), "upstart", logfile.c_str(), nullptr); |
1934 | + std::string path(cpath); |
1935 | + g_free(cpath); |
1936 | + |
1937 | + return path; |
1938 | +} |
1939 | + |
1940 | +/** Returns all the PIDs that are in the cgroup for this application */ |
1941 | +std::vector<pid_t> Upstart::pids() |
1942 | +{ |
1943 | + auto manager = std::dynamic_pointer_cast<manager::Upstart>(registry_->impl->jobs); |
1944 | + auto pids = manager->pidsFromCgroup(upstartName()); |
1945 | + g_debug("Got %d PIDs for AppID '%s'", int(pids.size()), std::string(appId_).c_str()); |
1946 | + return pids; |
1947 | +} |
1948 | + |
1949 | +/** Stops this instance by asking Upstart to stop it. Upstart will then |
1950 | + send a SIGTERM and five seconds later start killing things. */ |
1951 | +void Upstart::stop() |
1952 | +{ |
1953 | + if (!registry_->impl->thread.executeOnThread<bool>([this]() { |
1954 | + auto manager = std::dynamic_pointer_cast<manager::Upstart>(registry_->impl->jobs); |
1955 | + |
1956 | + g_debug("Stopping job %s app_id %s instance_id %s", job_.c_str(), std::string(appId_).c_str(), |
1957 | + instance_.c_str()); |
1958 | + |
1959 | + auto jobpath = manager->upstartJobPath(job_); |
1960 | + if (jobpath.empty()) |
1961 | + { |
1962 | + throw new std::runtime_error("Unable to get job path for Upstart job '" + job_ + "'"); |
1963 | + } |
1964 | + |
1965 | + GVariantBuilder builder; |
1966 | + g_variant_builder_init(&builder, G_VARIANT_TYPE_TUPLE); |
1967 | + g_variant_builder_open(&builder, G_VARIANT_TYPE_ARRAY); |
1968 | + |
1969 | + g_variant_builder_add_value( |
1970 | + &builder, g_variant_new_take_string(g_strdup_printf("APP_ID=%s", std::string(appId_).c_str()))); |
1971 | + |
1972 | + if (!instance_.empty()) |
1973 | + { |
1974 | + g_variant_builder_add_value( |
1975 | + &builder, g_variant_new_take_string(g_strdup_printf("INSTANCE_ID=%s", instance_.c_str()))); |
1976 | + } |
1977 | + |
1978 | + g_variant_builder_close(&builder); |
1979 | + g_variant_builder_add_value(&builder, g_variant_new_boolean(FALSE)); /* wait */ |
1980 | + |
1981 | + GError* error = nullptr; |
1982 | + GVariant* stop_variant = |
1983 | + g_dbus_connection_call_sync(registry_->impl->_dbus.get(), /* Dbus */ |
1984 | + DBUS_SERVICE_UPSTART, /* Upstart name */ |
1985 | + jobpath.c_str(), /* path */ |
1986 | + DBUS_INTERFACE_UPSTART_JOB, /* interface */ |
1987 | + "Stop", /* method */ |
1988 | + g_variant_builder_end(&builder), /* params */ |
1989 | + nullptr, /* return */ |
1990 | + G_DBUS_CALL_FLAGS_NONE, /* flags */ |
1991 | + -1, /* timeout: default */ |
1992 | + registry_->impl->thread.getCancellable().get(), /* cancellable */ |
1993 | + &error); /* error (hopefully not) */ |
1994 | + |
1995 | + g_clear_pointer(&stop_variant, g_variant_unref); |
1996 | + |
1997 | + if (error != nullptr) |
1998 | + { |
1999 | + g_warning("Unable to stop job %s app_id %s instance_id %s: %s", job_.c_str(), |
2000 | + std::string(appId_).c_str(), instance_.c_str(), error->message); |
2001 | + g_error_free(error); |
2002 | + return false; |
2003 | + } |
2004 | + |
2005 | + return true; |
2006 | + })) |
2007 | + { |
2008 | + g_warning("Unable to stop Upstart instance"); |
2009 | + } |
2010 | +} |
2011 | + |
2012 | +/** Create a new Upstart Instance object that can track the job and |
2013 | + get information about it. |
2014 | + |
2015 | + \param appId Application ID |
2016 | + \param job Upstart job name |
2017 | + \param instance Upstart instance name |
2018 | + \param urls URLs sent to the application (only on launch today) |
2019 | + \param registry Registry of persistent connections to use |
2020 | +*/ |
2021 | +Upstart::Upstart(const AppID& appId, |
2022 | + const std::string& job, |
2023 | + const std::string& instance, |
2024 | + const std::vector<Application::URL>& urls, |
2025 | + const std::shared_ptr<Registry>& registry) |
2026 | + : Base(appId, job, instance, urls, registry) |
2027 | +{ |
2028 | + g_debug("Creating a new Upstart for '%s' instance '%s'", std::string(appId).c_str(), instance.c_str()); |
2029 | +} |
2030 | + |
2031 | +/** Reformat a C++ vector of URLs into a C GStrv of strings |
2032 | + |
2033 | + \param urls Vector of URLs to make into C strings |
2034 | +*/ |
2035 | +std::shared_ptr<gchar*> Upstart::urlsToStrv(const std::vector<Application::URL>& urls) |
2036 | +{ |
2037 | + if (urls.empty()) |
2038 | + { |
2039 | + return {}; |
2040 | + } |
2041 | + |
2042 | + auto array = g_array_new(TRUE, FALSE, sizeof(gchar*)); |
2043 | + |
2044 | + for (auto url : urls) |
2045 | + { |
2046 | + auto str = g_strdup(url.value().c_str()); |
2047 | + g_debug("Converting URL: %s", str); |
2048 | + g_array_append_val(array, str); |
2049 | + } |
2050 | + |
2051 | + return std::shared_ptr<gchar*>((gchar**)g_array_free(array, FALSE), g_strfreev); |
2052 | +} |
2053 | + |
2054 | +/** Small helper that we can new/delete to work better with C stuff */ |
2055 | +struct StartCHelper |
2056 | +{ |
2057 | + std::shared_ptr<Upstart> ptr; |
2058 | +}; |
2059 | + |
2060 | +/** Callback from starting an application. It checks to see whether the |
2061 | + app is already running. If it is already running then we need to send |
2062 | + the URLs to it via DBus. |
2063 | + |
2064 | + \param obj The GDBusConnection object |
2065 | + \param res Async result object |
2066 | + \param user_data A pointer to a StartCHelper structure |
2067 | +*/ |
2068 | +void Upstart::application_start_cb(GObject* obj, GAsyncResult* res, gpointer user_data) |
2069 | +{ |
2070 | + auto data = static_cast<StartCHelper*>(user_data); |
2071 | + GError* error{nullptr}; |
2072 | + GVariant* result{nullptr}; |
2073 | + |
2074 | + tracepoint(ubuntu_app_launch, libual_start_message_callback, std::string(data->ptr->appId_).c_str()); |
2075 | + |
2076 | + g_debug("Started Message Callback: %s", std::string(data->ptr->appId_).c_str()); |
2077 | + |
2078 | + result = g_dbus_connection_call_finish(G_DBUS_CONNECTION(obj), res, &error); |
2079 | + |
2080 | + g_clear_pointer(&result, g_variant_unref); |
2081 | + |
2082 | + if (error != nullptr) |
2083 | + { |
2084 | + if (g_dbus_error_is_remote_error(error)) |
2085 | + { |
2086 | + gchar* remote_error = g_dbus_error_get_remote_error(error); |
2087 | + g_debug("Remote error: %s", remote_error); |
2088 | + if (g_strcmp0(remote_error, "com.ubuntu.Upstart0_6.Error.AlreadyStarted") == 0) |
2089 | + { |
2090 | + auto urls = urlsToStrv(data->ptr->urls_); |
2091 | + second_exec(data->ptr->registry_->impl->_dbus.get(), /* DBus */ |
2092 | + data->ptr->registry_->impl->thread.getCancellable().get(), /* cancellable */ |
2093 | + data->ptr->primaryPid(), /* primary pid */ |
2094 | + std::string(data->ptr->appId_).c_str(), /* appid */ |
2095 | + urls.get()); /* urls */ |
2096 | + } |
2097 | + |
2098 | + g_free(remote_error); |
2099 | + } |
2100 | + else |
2101 | + { |
2102 | + g_warning("Unable to emit event to start application: %s", error->message); |
2103 | + } |
2104 | + g_error_free(error); |
2105 | + } |
2106 | + |
2107 | + delete data; |
2108 | +} |
2109 | + |
2110 | +std::string Upstart::upstartJobPath(const std::string& job) |
2111 | +{ |
2112 | + auto manager = std::dynamic_pointer_cast<manager::Upstart>(registry_->impl->jobs); |
2113 | + return manager->upstartJobPath(job); |
2114 | +} |
2115 | + |
2116 | +} // namespace instances |
2117 | + |
2118 | +namespace manager |
2119 | +{ |
2120 | + |
2121 | +Upstart::Upstart(std::shared_ptr<Registry> registry) |
2122 | + : Base(registry) |
2123 | +{ |
2124 | +} |
2125 | + |
2126 | +Upstart::~Upstart() |
2127 | +{ |
2128 | +} |
2129 | + |
2130 | +/** Launch an application and create a new Upstart instance object to track |
2131 | + its progress. |
2132 | + |
2133 | + \param appId Application ID |
2134 | + \param job Upstart job name |
2135 | + \param instance Upstart instance name |
2136 | + \param urls URLs sent to the application (only on launch today) |
2137 | + \param mode Whether or not to setup the environment for testing |
2138 | + \param getenv A function to get additional environment variable when appropriate |
2139 | +*/ |
2140 | +std::shared_ptr<Application::Instance> Upstart::launch( |
2141 | + const AppID& appId, |
2142 | + const std::string& job, |
2143 | + const std::string& instance, |
2144 | + const std::vector<Application::URL>& urls, |
2145 | + launchMode mode, |
2146 | + std::function<std::list<std::pair<std::string, std::string>>(void)>& getenv) |
2147 | +{ |
2148 | + if (appId.empty()) |
2149 | + return {}; |
2150 | + |
2151 | + auto registry = registry_.lock(); |
2152 | + return registry->impl->thread.executeOnThread<std::shared_ptr<instance::Upstart>>( |
2153 | + [&]() -> std::shared_ptr<instance::Upstart> { |
2154 | + auto manager = std::dynamic_pointer_cast<manager::Upstart>(registry->impl->jobs); |
2155 | + std::string appIdStr{appId}; |
2156 | + g_debug("Initializing params for an new instance::Upstart for: %s", appIdStr.c_str()); |
2157 | + |
2158 | + tracepoint(ubuntu_app_launch, libual_start, appIdStr.c_str()); |
2159 | + |
2160 | + int timeout = 1; |
2161 | + if (ubuntu::app_launch::Registry::Impl::isWatchingAppStarting()) |
2162 | + { |
2163 | + timeout = 0; |
2164 | + } |
2165 | + |
2166 | + auto handshake = starting_handshake_start(appIdStr.c_str(), timeout); |
2167 | + if (handshake == nullptr) |
2168 | + { |
2169 | + g_warning("Unable to setup starting handshake"); |
2170 | + } |
2171 | + |
2172 | + /* Figure out the DBus path for the job */ |
2173 | + auto jobpath = manager->upstartJobPath(job); |
2174 | + |
2175 | + /* Build up our environment */ |
2176 | + auto env = getenv(); |
2177 | + |
2178 | + env.emplace_back(std::make_pair("APP_ID", appIdStr)); /* Application ID */ |
2179 | + env.emplace_back(std::make_pair("APP_LAUNCHER_PID", std::to_string(getpid()))); /* Who we are, for bugs */ |
2180 | + |
2181 | + if (!urls.empty()) |
2182 | + { |
2183 | + auto accumfunc = [](const std::string& prev, Application::URL thisurl) -> std::string { |
2184 | + gchar* gescaped = g_shell_quote(thisurl.value().c_str()); |
2185 | + std::string escaped; |
2186 | + if (gescaped != nullptr) |
2187 | + { |
2188 | + escaped = gescaped; |
2189 | + g_free(gescaped); |
2190 | + } |
2191 | + else |
2192 | + { |
2193 | + g_warning("Unable to escape URL: %s", thisurl.value().c_str()); |
2194 | + return prev; |
2195 | + } |
2196 | + |
2197 | + if (prev.empty()) |
2198 | + { |
2199 | + return escaped; |
2200 | + } |
2201 | + else |
2202 | + { |
2203 | + return prev + " " + escaped; |
2204 | + } |
2205 | + }; |
2206 | + auto urlstring = std::accumulate(urls.begin(), urls.end(), std::string{}, accumfunc); |
2207 | + env.emplace_back(std::make_pair("APP_URIS", urlstring)); |
2208 | + } |
2209 | + |
2210 | + if (mode == launchMode::TEST) |
2211 | + { |
2212 | + env.emplace_back(std::make_pair("QT_LOAD_TESTABILITY", "1")); |
2213 | + } |
2214 | + |
2215 | + /* Convert to GVariant */ |
2216 | + GVariantBuilder builder; |
2217 | + g_variant_builder_init(&builder, G_VARIANT_TYPE_TUPLE); |
2218 | + |
2219 | + g_variant_builder_open(&builder, G_VARIANT_TYPE_ARRAY); |
2220 | + |
2221 | + for (const auto& envvar : env) |
2222 | + { |
2223 | + g_variant_builder_add_value(&builder, g_variant_new_take_string(g_strdup_printf( |
2224 | + "%s=%s", envvar.first.c_str(), envvar.second.c_str()))); |
2225 | + } |
2226 | + |
2227 | + g_variant_builder_close(&builder); |
2228 | + g_variant_builder_add_value(&builder, g_variant_new_boolean(TRUE)); |
2229 | + |
2230 | + auto retval = std::make_shared<instance::Upstart>(appId, job, instance, urls, registry); |
2231 | + auto chelper = new instance::StartCHelper{}; |
2232 | + chelper->ptr = retval; |
2233 | + |
2234 | + tracepoint(ubuntu_app_launch, handshake_wait, appIdStr.c_str()); |
2235 | + starting_handshake_wait(handshake); |
2236 | + tracepoint(ubuntu_app_launch, handshake_complete, appIdStr.c_str()); |
2237 | + |
2238 | + /* Call the job start function */ |
2239 | + g_debug("Asking Upstart to start task for: %s", appIdStr.c_str()); |
2240 | + g_dbus_connection_call(registry->impl->_dbus.get(), /* bus */ |
2241 | + DBUS_SERVICE_UPSTART, /* service name */ |
2242 | + jobpath.c_str(), /* Path */ |
2243 | + DBUS_INTERFACE_UPSTART_JOB, /* interface */ |
2244 | + "Start", /* method */ |
2245 | + g_variant_builder_end(&builder), /* params */ |
2246 | + nullptr, /* return */ |
2247 | + G_DBUS_CALL_FLAGS_NONE, /* flags */ |
2248 | + -1, /* default timeout */ |
2249 | + registry->impl->thread.getCancellable().get(), /* cancellable */ |
2250 | + instance::Upstart::application_start_cb, /* callback */ |
2251 | + chelper /* object */ |
2252 | + ); |
2253 | + |
2254 | + tracepoint(ubuntu_app_launch, libual_start_message_sent, appIdStr.c_str()); |
2255 | + |
2256 | + return retval; |
2257 | + }); |
2258 | +} |
2259 | + |
2260 | +/** Special characters that could be an application name that |
2261 | + would activate in a regex */ |
2262 | +const static std::regex regexCharacters("([\\.\\-])"); |
2263 | + |
2264 | +std::shared_ptr<Application::Instance> Upstart::existing(const AppID& appId, |
2265 | + const std::string& job, |
2266 | + const std::string& instance, |
2267 | + const std::vector<Application::URL>& urls) |
2268 | +{ |
2269 | + return std::make_shared<instance::Upstart>(appId, job, instance, urls, registry_.lock()); |
2270 | +} |
2271 | + |
2272 | +std::vector<std::shared_ptr<instance::Base>> Upstart::instances(const AppID& appID, const std::string& job) |
2273 | +{ |
2274 | + std::vector<std::shared_ptr<instance::Base>> vect; |
2275 | + auto startsWith = std::string(appID); |
2276 | + if (job != "application-click") |
2277 | + { |
2278 | + startsWith += "-"; |
2279 | + } |
2280 | + |
2281 | + auto regexstr = |
2282 | + std::string{"^(?:"} + std::regex_replace(startsWith, regexCharacters, "\\$&") + std::string{")(\\d*)$"}; |
2283 | + auto instanceRegex = std::regex(regexstr); |
2284 | + |
2285 | + for (auto instance : upstartInstancesForJob(job)) |
2286 | + { |
2287 | + std::smatch instanceMatch; |
2288 | + g_debug("Looking at job '%s' instance: %s", job.c_str(), instance.c_str()); |
2289 | + if (std::regex_match(instance, instanceMatch, instanceRegex)) |
2290 | + { |
2291 | + auto app = existing(appID, job, instanceMatch[1].str(), {}); |
2292 | + vect.emplace_back(std::dynamic_pointer_cast<instance::Base>(app)); |
2293 | + } |
2294 | + } |
2295 | + |
2296 | + g_debug("App '%s' has %d instances", std::string(appID).c_str(), int(vect.size())); |
2297 | + |
2298 | + return vect; |
2299 | +} |
2300 | + |
2301 | +/** Initialize the CGManager connection, including a timeout to disconnect |
2302 | + as CGManager doesn't free resources entirely well. So it's better if |
2303 | + we connect and disconnect occationally */ |
2304 | +void Upstart::initCGManager() |
2305 | +{ |
2306 | + if (cgManager_) |
2307 | + return; |
2308 | + |
2309 | + std::promise<std::shared_ptr<GDBusConnection>> promise; |
2310 | + auto future = promise.get_future(); |
2311 | + auto registry = registry_.lock(); |
2312 | + |
2313 | + registry->impl->thread.executeOnThread([this, &promise, ®istry]() { |
2314 | + bool use_session_bus = g_getenv("UBUNTU_APP_LAUNCH_CG_MANAGER_SESSION_BUS") != nullptr; |
2315 | + if (use_session_bus) |
2316 | + { |
2317 | + /* For working dbusmock */ |
2318 | + g_debug("Connecting to CG Manager on session bus"); |
2319 | + promise.set_value(registry->impl->_dbus); |
2320 | + return; |
2321 | + } |
2322 | + |
2323 | + auto cancel = |
2324 | + std::shared_ptr<GCancellable>(g_cancellable_new(), [](GCancellable* cancel) { g_clear_object(&cancel); }); |
2325 | + |
2326 | + /* Ensure that we do not wait for more than a second */ |
2327 | + registry->impl->thread.timeoutSeconds(std::chrono::seconds{1}, |
2328 | + [cancel]() { g_cancellable_cancel(cancel.get()); }); |
2329 | + |
2330 | + g_dbus_connection_new_for_address( |
2331 | + CGMANAGER_DBUS_PATH, /* cgmanager path */ |
2332 | + G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, /* flags */ |
2333 | + nullptr, /* Auth Observer */ |
2334 | + cancel.get(), /* Cancellable */ |
2335 | + [](GObject* obj, GAsyncResult* res, gpointer data) -> void { |
2336 | + GError* error = nullptr; |
2337 | + auto promise = reinterpret_cast<std::promise<std::shared_ptr<GDBusConnection>>*>(data); |
2338 | + |
2339 | + auto gcon = g_dbus_connection_new_for_address_finish(res, &error); |
2340 | + if (error != nullptr) |
2341 | + { |
2342 | + g_error_free(error); |
2343 | + } |
2344 | + |
2345 | + auto con = std::shared_ptr<GDBusConnection>(gcon, [](GDBusConnection* con) { g_clear_object(&con); }); |
2346 | + promise->set_value(con); |
2347 | + }, |
2348 | + &promise); |
2349 | + }); |
2350 | + |
2351 | + cgManager_ = future.get(); |
2352 | + registry->impl->thread.timeoutSeconds(std::chrono::seconds{10}, [this]() { cgManager_.reset(); }); |
2353 | +} |
2354 | + |
2355 | +/** Get a list of PIDs from a CGroup, uses the CGManager connection to list |
2356 | + all of the PIDs. It is important to note that this is an IPC call, so it can |
2357 | + by its nature, be racy. Once the message has been sent the group can change. |
2358 | + You should take that into account in your usage of it. */ |
2359 | +std::vector<pid_t> Upstart::pidsFromCgroup(const std::string& jobpath) |
2360 | +{ |
2361 | + initCGManager(); |
2362 | + auto lmanager = cgManager_; /* Grab a local copy so we ensure it lasts through our lifetime */ |
2363 | + auto registry = registry_.lock(); |
2364 | + |
2365 | + return registry->impl->thread.executeOnThread<std::vector<pid_t>>([&jobpath, lmanager]() -> std::vector<pid_t> { |
2366 | + GError* error = nullptr; |
2367 | + const gchar* name = g_getenv("UBUNTU_APP_LAUNCH_CG_MANAGER_NAME"); |
2368 | + std::string groupname; |
2369 | + if (!jobpath.empty()) |
2370 | + { |
2371 | + groupname = "upstart/" + jobpath; |
2372 | + } |
2373 | + |
2374 | + g_debug("Looking for cg manager '%s' group '%s'", name, groupname.c_str()); |
2375 | + |
2376 | + GVariant* vtpids = g_dbus_connection_call_sync( |
2377 | + lmanager.get(), /* connection */ |
2378 | + name, /* bus name for direct connection is NULL */ |
2379 | + "/org/linuxcontainers/cgmanager", /* object */ |
2380 | + "org.linuxcontainers.cgmanager0_0", /* interface */ |
2381 | + "GetTasksRecursive", /* method */ |
2382 | + g_variant_new("(ss)", "freezer", groupname.empty() ? "" : groupname.c_str()), /* params */ |
2383 | + G_VARIANT_TYPE("(ai)"), /* output */ |
2384 | + G_DBUS_CALL_FLAGS_NONE, /* flags */ |
2385 | + -1, /* default timeout */ |
2386 | + nullptr, /* cancellable */ |
2387 | + &error); /* error */ |
2388 | + |
2389 | + if (error != nullptr) |
2390 | + { |
2391 | + g_warning("Unable to get PID list from cgroup manager: %s", error->message); |
2392 | + g_error_free(error); |
2393 | + return {}; |
2394 | + } |
2395 | + |
2396 | + GVariant* vpids = g_variant_get_child_value(vtpids, 0); |
2397 | + GVariantIter iter; |
2398 | + g_variant_iter_init(&iter, vpids); |
2399 | + gint32 pid; |
2400 | + std::vector<pid_t> pids; |
2401 | + |
2402 | + while (g_variant_iter_loop(&iter, "i", &pid)) |
2403 | + { |
2404 | + pids.push_back(pid); |
2405 | + } |
2406 | + |
2407 | + g_variant_unref(vpids); |
2408 | + g_variant_unref(vtpids); |
2409 | + |
2410 | + return pids; |
2411 | + }); |
2412 | +} |
2413 | + |
2414 | +/** Looks to find the Upstart object path for a specific Upstart job. This first |
2415 | + checks the cache, and otherwise does the lookup on DBus. */ |
2416 | +std::string Upstart::upstartJobPath(const std::string& job) |
2417 | +{ |
2418 | + try |
2419 | + { |
2420 | + return upstartJobPathCache_.at(job); |
2421 | + } |
2422 | + catch (std::out_of_range& e) |
2423 | + { |
2424 | + auto registry = registry_.lock(); |
2425 | + auto path = registry->impl->thread.executeOnThread<std::string>([this, &job, ®istry]() -> std::string { |
2426 | + GError* error = nullptr; |
2427 | + GVariant* job_path_variant = |
2428 | + g_dbus_connection_call_sync(registry->impl->_dbus.get(), /* connection */ |
2429 | + DBUS_SERVICE_UPSTART, /* service */ |
2430 | + DBUS_PATH_UPSTART, /* path */ |
2431 | + DBUS_INTERFACE_UPSTART, /* iface */ |
2432 | + "GetJobByName", /* method */ |
2433 | + g_variant_new("(s)", job.c_str()), /* params */ |
2434 | + G_VARIANT_TYPE("(o)"), /* return */ |
2435 | + G_DBUS_CALL_FLAGS_NONE, /* flags */ |
2436 | + -1, /* timeout: default */ |
2437 | + registry->impl->thread.getCancellable().get(), /* cancellable */ |
2438 | + &error); /* error */ |
2439 | + |
2440 | + if (error != nullptr) |
2441 | + { |
2442 | + g_warning("Unable to find job '%s': %s", job.c_str(), error->message); |
2443 | + g_error_free(error); |
2444 | + return {}; |
2445 | + } |
2446 | + |
2447 | + gchar* job_path = nullptr; |
2448 | + g_variant_get(job_path_variant, "(o)", &job_path); |
2449 | + g_variant_unref(job_path_variant); |
2450 | + |
2451 | + if (job_path != nullptr) |
2452 | + { |
2453 | + std::string path(job_path); |
2454 | + g_free(job_path); |
2455 | + return path; |
2456 | + } |
2457 | + else |
2458 | + { |
2459 | + return {}; |
2460 | + } |
2461 | + }); |
2462 | + |
2463 | + upstartJobPathCache_[job] = path; |
2464 | + return path; |
2465 | + } |
2466 | +} |
2467 | + |
2468 | +/** Queries Upstart to get all the instances of a given job. This |
2469 | + can take a while as the number of dbus calls is n+1. It is |
2470 | + rare that apps have many instances though. */ |
2471 | +std::list<std::string> Upstart::upstartInstancesForJob(const std::string& job) |
2472 | +{ |
2473 | + std::string jobpath = upstartJobPath(job); |
2474 | + if (jobpath.empty()) |
2475 | + { |
2476 | + return {}; |
2477 | + } |
2478 | + |
2479 | + auto registry = registry_.lock(); |
2480 | + return registry->impl->thread.executeOnThread<std::list<std::string>>( |
2481 | + [this, &job, &jobpath, ®istry]() -> std::list<std::string> { |
2482 | + GError* error = nullptr; |
2483 | + GVariant* instance_tuple = |
2484 | + g_dbus_connection_call_sync(registry->impl->_dbus.get(), /* connection */ |
2485 | + DBUS_SERVICE_UPSTART, /* service */ |
2486 | + jobpath.c_str(), /* object path */ |
2487 | + DBUS_INTERFACE_UPSTART_JOB, /* iface */ |
2488 | + "GetAllInstances", /* method */ |
2489 | + nullptr, /* params */ |
2490 | + G_VARIANT_TYPE("(ao)"), /* return type */ |
2491 | + G_DBUS_CALL_FLAGS_NONE, /* flags */ |
2492 | + -1, /* timeout: default */ |
2493 | + registry->impl->thread.getCancellable().get(), /* cancellable */ |
2494 | + &error); |
2495 | + |
2496 | + if (error != nullptr) |
2497 | + { |
2498 | + g_warning("Unable to get instances of job '%s': %s", job.c_str(), error->message); |
2499 | + g_error_free(error); |
2500 | + return {}; |
2501 | + } |
2502 | + |
2503 | + if (instance_tuple == nullptr) |
2504 | + { |
2505 | + return {}; |
2506 | + } |
2507 | + |
2508 | + GVariant* instance_list = g_variant_get_child_value(instance_tuple, 0); |
2509 | + g_variant_unref(instance_tuple); |
2510 | + |
2511 | + GVariantIter instance_iter; |
2512 | + g_variant_iter_init(&instance_iter, instance_list); |
2513 | + const gchar* instance_path = nullptr; |
2514 | + std::list<std::string> instances; |
2515 | + |
2516 | + while (g_variant_iter_loop(&instance_iter, "&o", &instance_path)) |
2517 | + { |
2518 | + GVariant* props_tuple = |
2519 | + g_dbus_connection_call_sync(registry->impl->_dbus.get(), /* connection */ |
2520 | + DBUS_SERVICE_UPSTART, /* service */ |
2521 | + instance_path, /* object path */ |
2522 | + "org.freedesktop.DBus.Properties", /* interface */ |
2523 | + "GetAll", /* method */ |
2524 | + g_variant_new("(s)", DBUS_INTERFACE_UPSTART_INSTANCE), /* params */ |
2525 | + G_VARIANT_TYPE("(a{sv})"), /* return type */ |
2526 | + G_DBUS_CALL_FLAGS_NONE, /* flags */ |
2527 | + -1, /* timeout: default */ |
2528 | + registry->impl->thread.getCancellable().get(), /* cancellable */ |
2529 | + &error); |
2530 | + |
2531 | + if (error != nullptr) |
2532 | + { |
2533 | + g_warning("Unable to name of instance '%s': %s", instance_path, error->message); |
2534 | + g_error_free(error); |
2535 | + error = nullptr; |
2536 | + continue; |
2537 | + } |
2538 | + |
2539 | + GVariant* props_dict = g_variant_get_child_value(props_tuple, 0); |
2540 | + |
2541 | + GVariant* namev = g_variant_lookup_value(props_dict, "name", G_VARIANT_TYPE_STRING); |
2542 | + if (namev != nullptr) |
2543 | + { |
2544 | + auto name = g_variant_get_string(namev, NULL); |
2545 | + g_debug("Adding instance for job '%s': %s", job.c_str(), name); |
2546 | + instances.push_back(name); |
2547 | + g_variant_unref(namev); |
2548 | + } |
2549 | + |
2550 | + g_variant_unref(props_dict); |
2551 | + g_variant_unref(props_tuple); |
2552 | + } |
2553 | + |
2554 | + g_variant_unref(instance_list); |
2555 | + |
2556 | + return instances; |
2557 | + }); |
2558 | +} |
2559 | + |
2560 | +std::list<std::shared_ptr<Application>> Upstart::runningApps() |
2561 | +{ |
2562 | + std::list<std::string> instances; |
2563 | + |
2564 | + /* Get all the legacy instances */ |
2565 | + instances.splice(instances.begin(), upstartInstancesForJob("application-legacy")); |
2566 | + /* Get all the snap instances */ |
2567 | + instances.splice(instances.begin(), upstartInstancesForJob("application-snap")); |
2568 | + |
2569 | + /* Remove the instance ID */ |
2570 | + std::transform(instances.begin(), instances.end(), instances.begin(), [](std::string& instancename) -> std::string { |
2571 | + static const std::regex instanceregex("^(.*)-[0-9]*$"); |
2572 | + std::smatch match; |
2573 | + if (std::regex_match(instancename, match, instanceregex)) |
2574 | + { |
2575 | + return match[1].str(); |
2576 | + } |
2577 | + else |
2578 | + { |
2579 | + g_warning("Unable to match instance name: %s", instancename.c_str()); |
2580 | + return {}; |
2581 | + } |
2582 | + }); |
2583 | + |
2584 | + /* Deduplicate Set */ |
2585 | + std::set<std::string> instanceset; |
2586 | + for (auto instance : instances) |
2587 | + { |
2588 | + if (!instance.empty()) |
2589 | + instanceset.insert(instance); |
2590 | + } |
2591 | + |
2592 | + /* Add in the click instances */ |
2593 | + for (auto instance : upstartInstancesForJob("application-click")) |
2594 | + { |
2595 | + instanceset.insert(instance); |
2596 | + } |
2597 | + |
2598 | + g_debug("Overall there are %d instances: %s", int(instanceset.size()), |
2599 | + std::accumulate(instanceset.begin(), instanceset.end(), std::string{}, |
2600 | + [](const std::string& instr, std::string instance) { |
2601 | + return instr.empty() ? instance : instr + ", " + instance; |
2602 | + }) |
2603 | + .c_str()); |
2604 | + |
2605 | + /* Convert to Applications */ |
2606 | + auto registry = registry_.lock(); |
2607 | + std::list<std::shared_ptr<Application>> apps; |
2608 | + for (auto instance : instanceset) |
2609 | + { |
2610 | + auto appid = AppID::find(registry, instance); |
2611 | + auto app = Application::create(appid, registry); |
2612 | + apps.push_back(app); |
2613 | + } |
2614 | + |
2615 | + return apps; |
2616 | +} |
2617 | + |
2618 | +} // namespace manager |
2619 | +} // namespace jobs |
2620 | +} // namespace app_launch |
2621 | +} // namespace ubuntu |
2622 | |
2623 | === added file 'libubuntu-app-launch/jobs-upstart.h' |
2624 | --- libubuntu-app-launch/jobs-upstart.h 1970-01-01 00:00:00 +0000 |
2625 | +++ libubuntu-app-launch/jobs-upstart.h 2016-11-10 21:58:30 +0000 |
2626 | @@ -0,0 +1,75 @@ |
2627 | +/* |
2628 | + * Copyright © 2016 Canonical Ltd. |
2629 | + * |
2630 | + * This program is free software: you can redistribute it and/or modify it |
2631 | + * under the terms of the GNU General Public License version 3, as published |
2632 | + * by the Free Software Foundation. |
2633 | + * |
2634 | + * This program is distributed in the hope that it will be useful, but |
2635 | + * WITHOUT ANY WARRANTY; without even the implied warranties of |
2636 | + * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
2637 | + * PURPOSE. See the GNU General Public License for more details. |
2638 | + * |
2639 | + * You should have received a copy of the GNU General Public License along |
2640 | + * with this program. If not, see <http://www.gnu.org/licenses/>. |
2641 | + * |
2642 | + * Authors: |
2643 | + * Ted Gould <ted.gould@canonical.com> |
2644 | + */ |
2645 | + |
2646 | +#pragma once |
2647 | + |
2648 | +#include "jobs-base.h" |
2649 | +#include <gio/gio.h> |
2650 | +#include <map> |
2651 | + |
2652 | +namespace ubuntu |
2653 | +{ |
2654 | +namespace app_launch |
2655 | +{ |
2656 | +namespace jobs |
2657 | +{ |
2658 | +namespace manager |
2659 | +{ |
2660 | + |
2661 | +class Upstart : public Base |
2662 | +{ |
2663 | +public: |
2664 | + Upstart(std::shared_ptr<Registry> registry); |
2665 | + virtual ~Upstart(); |
2666 | + |
2667 | + virtual std::shared_ptr<Application::Instance> launch( |
2668 | + const AppID& appId, |
2669 | + const std::string& job, |
2670 | + const std::string& instance, |
2671 | + const std::vector<Application::URL>& urls, |
2672 | + launchMode mode, |
2673 | + std::function<std::list<std::pair<std::string, std::string>>(void)>& getenv) override; |
2674 | + virtual std::shared_ptr<Application::Instance> existing(const AppID& appId, |
2675 | + const std::string& job, |
2676 | + const std::string& instance, |
2677 | + const std::vector<Application::URL>& urls) override; |
2678 | + |
2679 | + virtual std::list<std::shared_ptr<Application>> runningApps() override; |
2680 | + |
2681 | + virtual std::vector<std::shared_ptr<instance::Base>> instances(const AppID& appID, const std::string& job) override; |
2682 | + |
2683 | + std::vector<pid_t> pidsFromCgroup(const std::string& jobpath); |
2684 | + |
2685 | + std::list<std::string> upstartInstancesForJob(const std::string& job); |
2686 | + std::string upstartJobPath(const std::string& job); |
2687 | + |
2688 | +private: |
2689 | + void initCGManager(); |
2690 | + |
2691 | + std::shared_ptr<GDBusConnection> cgManager_; |
2692 | + |
2693 | + /** Getting the Upstart job path is relatively expensive in |
2694 | + that it requires a DBus call. Worth keeping a cache of. */ |
2695 | + std::map<std::string, std::string> upstartJobPathCache_; |
2696 | +}; |
2697 | + |
2698 | +} // namespace manager |
2699 | +} // namespace jobs |
2700 | +} // namespace app_launch |
2701 | +} // namespace ubuntu |
2702 | |
2703 | === modified file 'libubuntu-app-launch/libubuntu-app-launch.map' |
2704 | --- libubuntu-app-launch/libubuntu-app-launch.map 2016-05-19 16:24:11 +0000 |
2705 | +++ libubuntu-app-launch/libubuntu-app-launch.map 2016-11-10 21:58:30 +0000 |
2706 | @@ -14,6 +14,7 @@ |
2707 | ubuntu::app_launch::Helper::*; |
2708 | typeinfo?for?ubuntu::app_launch::Helper; |
2709 | typeinfo?name?for?ubuntu::app_launch::Helper; |
2710 | + ubuntu::app_launch::operator*; |
2711 | ubuntu::app_launch::oom::*; |
2712 | }; |
2713 | local: |
2714 | |
2715 | === modified file 'libubuntu-app-launch/registry-impl.cpp' |
2716 | --- libubuntu-app-launch/registry-impl.cpp 2016-09-23 22:30:51 +0000 |
2717 | +++ libubuntu-app-launch/registry-impl.cpp 2016-11-10 21:58:30 +0000 |
2718 | @@ -19,7 +19,7 @@ |
2719 | |
2720 | #include "registry-impl.h" |
2721 | #include "application-icon-finder.h" |
2722 | -#include <cgmanager/cgmanager.h> |
2723 | +#include <regex> |
2724 | #include <upstart.h> |
2725 | |
2726 | namespace ubuntu |
2727 | @@ -34,7 +34,24 @@ |
2728 | _clickDB.reset(); |
2729 | |
2730 | zgLog_.reset(); |
2731 | - cgManager_.reset(); |
2732 | + jobs.reset(); |
2733 | + |
2734 | + auto dohandle = [&](guint& handle) { |
2735 | + if (handle != 0) |
2736 | + { |
2737 | + g_dbus_connection_signal_unsubscribe(_dbus.get(), handle); |
2738 | + handle = 0; |
2739 | + } |
2740 | + }; |
2741 | + |
2742 | + dohandle(handle_appStarted); |
2743 | + dohandle(handle_appStopped); |
2744 | + dohandle(handle_appFailed); |
2745 | + dohandle(handle_appPaused); |
2746 | + dohandle(handle_appResumed); |
2747 | + dohandle(handle_managerSignalFocus); |
2748 | + dohandle(handle_managerSignalResume); |
2749 | + dohandle(handle_managerSignalStarting); |
2750 | |
2751 | if (_dbus) |
2752 | g_dbus_connection_flush_sync(_dbus.get(), nullptr, nullptr); |
2753 | @@ -42,7 +59,6 @@ |
2754 | }) |
2755 | , _registry(registry) |
2756 | , _iconFinders() |
2757 | -// _manager(nullptr) |
2758 | { |
2759 | auto cancel = thread.getCancellable(); |
2760 | _dbus = thread.executeOnThread<std::shared_ptr<GDBusConnection>>([cancel]() { |
2761 | @@ -214,257 +230,6 @@ |
2762 | }); |
2763 | } |
2764 | |
2765 | -/** Initialize the CGManager connection, including a timeout to disconnect |
2766 | - as CGManager doesn't free resources entirely well. So it's better if |
2767 | - we connect and disconnect occationally */ |
2768 | -void Registry::Impl::initCGManager() |
2769 | -{ |
2770 | - if (cgManager_) |
2771 | - return; |
2772 | - |
2773 | - std::promise<std::shared_ptr<GDBusConnection>> promise; |
2774 | - auto future = promise.get_future(); |
2775 | - |
2776 | - thread.executeOnThread([this, &promise]() { |
2777 | - bool use_session_bus = g_getenv("UBUNTU_APP_LAUNCH_CG_MANAGER_SESSION_BUS") != nullptr; |
2778 | - if (use_session_bus) |
2779 | - { |
2780 | - /* For working dbusmock */ |
2781 | - g_debug("Connecting to CG Manager on session bus"); |
2782 | - promise.set_value(_dbus); |
2783 | - return; |
2784 | - } |
2785 | - |
2786 | - auto cancel = |
2787 | - std::shared_ptr<GCancellable>(g_cancellable_new(), [](GCancellable* cancel) { g_clear_object(&cancel); }); |
2788 | - |
2789 | - /* Ensure that we do not wait for more than a second */ |
2790 | - thread.timeoutSeconds(std::chrono::seconds{1}, [cancel]() { g_cancellable_cancel(cancel.get()); }); |
2791 | - |
2792 | - g_dbus_connection_new_for_address( |
2793 | - CGMANAGER_DBUS_PATH, /* cgmanager path */ |
2794 | - G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, /* flags */ |
2795 | - nullptr, /* Auth Observer */ |
2796 | - cancel.get(), /* Cancellable */ |
2797 | - [](GObject* obj, GAsyncResult* res, gpointer data) -> void { |
2798 | - GError* error = nullptr; |
2799 | - auto promise = reinterpret_cast<std::promise<std::shared_ptr<GDBusConnection>>*>(data); |
2800 | - |
2801 | - auto gcon = g_dbus_connection_new_for_address_finish(res, &error); |
2802 | - if (error != nullptr) |
2803 | - { |
2804 | - g_error_free(error); |
2805 | - } |
2806 | - |
2807 | - auto con = std::shared_ptr<GDBusConnection>(gcon, [](GDBusConnection* con) { g_clear_object(&con); }); |
2808 | - promise->set_value(con); |
2809 | - }, |
2810 | - &promise); |
2811 | - }); |
2812 | - |
2813 | - cgManager_ = future.get(); |
2814 | - thread.timeoutSeconds(std::chrono::seconds{10}, [this]() { cgManager_.reset(); }); |
2815 | -} |
2816 | - |
2817 | -/** Get a list of PIDs from a CGroup, uses the CGManager connection to list |
2818 | - all of the PIDs. It is important to note that this is an IPC call, so it can |
2819 | - by its nature, be racy. Once the message has been sent the group can change. |
2820 | - You should take that into account in your usage of it. */ |
2821 | -std::vector<pid_t> Registry::Impl::pidsFromCgroup(const std::string& jobpath) |
2822 | -{ |
2823 | - initCGManager(); |
2824 | - auto lmanager = cgManager_; /* Grab a local copy so we ensure it lasts through our lifetime */ |
2825 | - |
2826 | - return thread.executeOnThread<std::vector<pid_t>>([&jobpath, lmanager]() -> std::vector<pid_t> { |
2827 | - GError* error = nullptr; |
2828 | - const gchar* name = g_getenv("UBUNTU_APP_LAUNCH_CG_MANAGER_NAME"); |
2829 | - std::string groupname; |
2830 | - if (!jobpath.empty()) |
2831 | - { |
2832 | - groupname = "upstart/" + jobpath; |
2833 | - } |
2834 | - |
2835 | - g_debug("Looking for cg manager '%s' group '%s'", name, groupname.c_str()); |
2836 | - |
2837 | - GVariant* vtpids = g_dbus_connection_call_sync( |
2838 | - lmanager.get(), /* connection */ |
2839 | - name, /* bus name for direct connection is NULL */ |
2840 | - "/org/linuxcontainers/cgmanager", /* object */ |
2841 | - "org.linuxcontainers.cgmanager0_0", /* interface */ |
2842 | - "GetTasksRecursive", /* method */ |
2843 | - g_variant_new("(ss)", "freezer", groupname.empty() ? "" : groupname.c_str()), /* params */ |
2844 | - G_VARIANT_TYPE("(ai)"), /* output */ |
2845 | - G_DBUS_CALL_FLAGS_NONE, /* flags */ |
2846 | - -1, /* default timeout */ |
2847 | - nullptr, /* cancellable */ |
2848 | - &error); /* error */ |
2849 | - |
2850 | - if (error != nullptr) |
2851 | - { |
2852 | - g_warning("Unable to get PID list from cgroup manager: %s", error->message); |
2853 | - g_error_free(error); |
2854 | - return {}; |
2855 | - } |
2856 | - |
2857 | - GVariant* vpids = g_variant_get_child_value(vtpids, 0); |
2858 | - GVariantIter iter; |
2859 | - g_variant_iter_init(&iter, vpids); |
2860 | - gint32 pid; |
2861 | - std::vector<pid_t> pids; |
2862 | - |
2863 | - while (g_variant_iter_loop(&iter, "i", &pid)) |
2864 | - { |
2865 | - pids.push_back(pid); |
2866 | - } |
2867 | - |
2868 | - g_variant_unref(vpids); |
2869 | - g_variant_unref(vtpids); |
2870 | - |
2871 | - return pids; |
2872 | - }); |
2873 | -} |
2874 | - |
2875 | -/** Looks to find the Upstart object path for a specific Upstart job. This first |
2876 | - checks the cache, and otherwise does the lookup on DBus. */ |
2877 | -std::string Registry::Impl::upstartJobPath(const std::string& job) |
2878 | -{ |
2879 | - try |
2880 | - { |
2881 | - return upstartJobPathCache_.at(job); |
2882 | - } |
2883 | - catch (std::out_of_range& e) |
2884 | - { |
2885 | - auto path = thread.executeOnThread<std::string>([this, &job]() -> std::string { |
2886 | - GError* error = nullptr; |
2887 | - GVariant* job_path_variant = g_dbus_connection_call_sync(_dbus.get(), /* connection */ |
2888 | - DBUS_SERVICE_UPSTART, /* service */ |
2889 | - DBUS_PATH_UPSTART, /* path */ |
2890 | - DBUS_INTERFACE_UPSTART, /* iface */ |
2891 | - "GetJobByName", /* method */ |
2892 | - g_variant_new("(s)", job.c_str()), /* params */ |
2893 | - G_VARIANT_TYPE("(o)"), /* return */ |
2894 | - G_DBUS_CALL_FLAGS_NONE, /* flags */ |
2895 | - -1, /* timeout: default */ |
2896 | - thread.getCancellable().get(), /* cancellable */ |
2897 | - &error); /* error */ |
2898 | - |
2899 | - if (error != nullptr) |
2900 | - { |
2901 | - g_warning("Unable to find job '%s': %s", job.c_str(), error->message); |
2902 | - g_error_free(error); |
2903 | - return {}; |
2904 | - } |
2905 | - |
2906 | - gchar* job_path = nullptr; |
2907 | - g_variant_get(job_path_variant, "(o)", &job_path); |
2908 | - g_variant_unref(job_path_variant); |
2909 | - |
2910 | - if (job_path != nullptr) |
2911 | - { |
2912 | - std::string path(job_path); |
2913 | - g_free(job_path); |
2914 | - return path; |
2915 | - } |
2916 | - else |
2917 | - { |
2918 | - return {}; |
2919 | - } |
2920 | - }); |
2921 | - |
2922 | - upstartJobPathCache_[job] = path; |
2923 | - return path; |
2924 | - } |
2925 | -} |
2926 | - |
2927 | -/** Queries Upstart to get all the instances of a given job. This |
2928 | - can take a while as the number of dbus calls is n+1. It is |
2929 | - rare that apps have many instances though. */ |
2930 | -std::list<std::string> Registry::Impl::upstartInstancesForJob(const std::string& job) |
2931 | -{ |
2932 | - std::string jobpath = upstartJobPath(job); |
2933 | - if (jobpath.empty()) |
2934 | - { |
2935 | - return {}; |
2936 | - } |
2937 | - |
2938 | - return thread.executeOnThread<std::list<std::string>>([this, &job, &jobpath]() -> std::list<std::string> { |
2939 | - GError* error = nullptr; |
2940 | - GVariant* instance_tuple = g_dbus_connection_call_sync(_dbus.get(), /* connection */ |
2941 | - DBUS_SERVICE_UPSTART, /* service */ |
2942 | - jobpath.c_str(), /* object path */ |
2943 | - DBUS_INTERFACE_UPSTART_JOB, /* iface */ |
2944 | - "GetAllInstances", /* method */ |
2945 | - nullptr, /* params */ |
2946 | - G_VARIANT_TYPE("(ao)"), /* return type */ |
2947 | - G_DBUS_CALL_FLAGS_NONE, /* flags */ |
2948 | - -1, /* timeout: default */ |
2949 | - thread.getCancellable().get(), /* cancellable */ |
2950 | - &error); |
2951 | - |
2952 | - if (error != nullptr) |
2953 | - { |
2954 | - g_warning("Unable to get instances of job '%s': %s", job.c_str(), error->message); |
2955 | - g_error_free(error); |
2956 | - return {}; |
2957 | - } |
2958 | - |
2959 | - if (instance_tuple == nullptr) |
2960 | - { |
2961 | - return {}; |
2962 | - } |
2963 | - |
2964 | - GVariant* instance_list = g_variant_get_child_value(instance_tuple, 0); |
2965 | - g_variant_unref(instance_tuple); |
2966 | - |
2967 | - GVariantIter instance_iter; |
2968 | - g_variant_iter_init(&instance_iter, instance_list); |
2969 | - const gchar* instance_path = nullptr; |
2970 | - std::list<std::string> instances; |
2971 | - |
2972 | - while (g_variant_iter_loop(&instance_iter, "&o", &instance_path)) |
2973 | - { |
2974 | - GVariant* props_tuple = |
2975 | - g_dbus_connection_call_sync(_dbus.get(), /* connection */ |
2976 | - DBUS_SERVICE_UPSTART, /* service */ |
2977 | - instance_path, /* object path */ |
2978 | - "org.freedesktop.DBus.Properties", /* interface */ |
2979 | - "GetAll", /* method */ |
2980 | - g_variant_new("(s)", DBUS_INTERFACE_UPSTART_INSTANCE), /* params */ |
2981 | - G_VARIANT_TYPE("(a{sv})"), /* return type */ |
2982 | - G_DBUS_CALL_FLAGS_NONE, /* flags */ |
2983 | - -1, /* timeout: default */ |
2984 | - thread.getCancellable().get(), /* cancellable */ |
2985 | - &error); |
2986 | - |
2987 | - if (error != nullptr) |
2988 | - { |
2989 | - g_warning("Unable to name of instance '%s': %s", instance_path, error->message); |
2990 | - g_error_free(error); |
2991 | - error = nullptr; |
2992 | - continue; |
2993 | - } |
2994 | - |
2995 | - GVariant* props_dict = g_variant_get_child_value(props_tuple, 0); |
2996 | - |
2997 | - GVariant* namev = g_variant_lookup_value(props_dict, "name", G_VARIANT_TYPE_STRING); |
2998 | - if (namev != nullptr) |
2999 | - { |
3000 | - auto name = g_variant_get_string(namev, NULL); |
3001 | - g_debug("Adding instance for job '%s': %s", job.c_str(), name); |
3002 | - instances.push_back(name); |
3003 | - g_variant_unref(namev); |
3004 | - } |
3005 | - |
3006 | - g_variant_unref(props_dict); |
3007 | - g_variant_unref(props_tuple); |
3008 | - } |
3009 | - |
3010 | - g_variant_unref(instance_list); |
3011 | - |
3012 | - return instances; |
3013 | - }); |
3014 | -} |
3015 | - |
3016 | /** Send an event to Zietgeist using the registry thread so that |
3017 | the callback comes back in the right place. */ |
3018 | void Registry::Impl::zgSendEvent(AppID appid, const std::string& eventtype) |
3019 | @@ -536,24 +301,193 @@ |
3020 | return _iconFinders[basePath]; |
3021 | } |
3022 | |
3023 | -#if 0 |
3024 | -void |
3025 | -Registry::Impl::setManager (Registry::Manager* manager) |
3026 | -{ |
3027 | - if (_manager != nullptr) |
3028 | +/** Structure to track the data needed for upstart events. This cleans |
3029 | + up the lifecycle as we're passing this as a pointer through the |
3030 | + GLib calls. */ |
3031 | +struct upstartEventData |
3032 | +{ |
3033 | + /** Keeping a weak pointer because the handle is held by |
3034 | + the registry implementation. */ |
3035 | + std::weak_ptr<Registry> weakReg; |
3036 | +}; |
3037 | + |
3038 | +/** Take the GVariant of parameters and turn them into an application and |
3039 | + and instance. Easier to read in the smaller function */ |
3040 | +std::tuple<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>> Registry::Impl::managerParams( |
3041 | + const std::shared_ptr<GVariant>& params, const std::shared_ptr<Registry>& reg) |
3042 | +{ |
3043 | + std::shared_ptr<Application> app; |
3044 | + std::shared_ptr<Application::Instance> instance; |
3045 | + |
3046 | + const gchar* cappid = nullptr; |
3047 | + g_variant_get(params.get(), "(&s)", &cappid); |
3048 | + |
3049 | + auto appid = ubuntu::app_launch::AppID::find(reg, cappid); |
3050 | + app = ubuntu::app_launch::Application::create(appid, reg); |
3051 | + |
3052 | + return std::make_tuple(app, instance); |
3053 | +} |
3054 | + |
3055 | +/** Used to store data for manager based signal handlers. Has a link to the |
3056 | + registry and the callback to use in a C++ style. */ |
3057 | +struct managerEventData |
3058 | +{ |
3059 | + /* Keeping a weak pointer because the handle is held by |
3060 | + the registry implementation. */ |
3061 | + std::weak_ptr<Registry> weakReg; |
3062 | + std::function<void(const std::shared_ptr<Registry>& reg, |
3063 | + const std::shared_ptr<Application>& app, |
3064 | + const std::shared_ptr<Application::Instance>& instance, |
3065 | + const std::shared_ptr<GDBusConnection>&, |
3066 | + const std::string&, |
3067 | + const std::shared_ptr<GVariant>&)> |
3068 | + func; |
3069 | +}; |
3070 | + |
3071 | +/** Register for a signal for the manager. All of the signals needed this same |
3072 | + code so it got pulled out into a function. Takes the same of the signal, the registry |
3073 | + that we're using and a function to call after we've messaged all the parameters |
3074 | + into being something C++-ish. */ |
3075 | +guint Registry::Impl::managerSignalHelper(const std::shared_ptr<Registry>& reg, |
3076 | + const std::string& signalname, |
3077 | + std::function<void(const std::shared_ptr<Registry>& reg, |
3078 | + const std::shared_ptr<Application>& app, |
3079 | + const std::shared_ptr<Application::Instance>& instance, |
3080 | + const std::shared_ptr<GDBusConnection>&, |
3081 | + const std::string&, |
3082 | + const std::shared_ptr<GVariant>&)> responsefunc) |
3083 | +{ |
3084 | + managerEventData* focusdata = new managerEventData{reg, responsefunc}; |
3085 | + |
3086 | + return g_dbus_connection_signal_subscribe( |
3087 | + reg->impl->_dbus.get(), /* bus */ |
3088 | + nullptr, /* sender */ |
3089 | + "com.canonical.UbuntuAppLaunch", /* interface */ |
3090 | + signalname.c_str(), /* signal */ |
3091 | + "/", /* path */ |
3092 | + nullptr, /* arg0 */ |
3093 | + G_DBUS_SIGNAL_FLAGS_NONE, |
3094 | + [](GDBusConnection* cconn, const gchar* csender, const gchar*, const gchar*, const gchar*, GVariant* params, |
3095 | + gpointer user_data) -> void { |
3096 | + auto data = reinterpret_cast<managerEventData*>(user_data); |
3097 | + auto reg = data->weakReg.lock(); |
3098 | + |
3099 | + /* If we're still conneted and the manager has been cleared |
3100 | + we'll just be a no-op */ |
3101 | + if (!reg->impl->manager_) |
3102 | + { |
3103 | + return; |
3104 | + } |
3105 | + |
3106 | + auto vparams = std::shared_ptr<GVariant>(g_variant_ref(params), g_variant_unref); |
3107 | + auto conn = std::shared_ptr<GDBusConnection>(reinterpret_cast<GDBusConnection*>(g_object_ref(cconn)), |
3108 | + [](GDBusConnection* con) { g_clear_object(&con); }); |
3109 | + std::string sender = csender; |
3110 | + std::shared_ptr<Application> app; |
3111 | + std::shared_ptr<Application::Instance> instance; |
3112 | + |
3113 | + std::tie(app, instance) = managerParams(vparams, reg); |
3114 | + |
3115 | + data->func(reg, app, instance, conn, sender, vparams); |
3116 | + }, |
3117 | + focusdata, |
3118 | + [](gpointer user_data) { |
3119 | + auto data = reinterpret_cast<managerEventData*>(user_data); |
3120 | + delete data; |
3121 | + }); /* user data destroy */ |
3122 | +} |
3123 | + |
3124 | +/** Set the manager for the registry. This includes tracking the pointer |
3125 | + as well as setting up the signals to call back into the manager. The |
3126 | + signals are only setup once per registry even if the manager is cleared |
3127 | + and changed again. They will just be no-op's in those cases. |
3128 | +*/ |
3129 | +void Registry::Impl::setManager(std::shared_ptr<Registry::Manager> manager, std::shared_ptr<Registry> reg) |
3130 | +{ |
3131 | + if (reg->impl->manager_) |
3132 | { |
3133 | throw std::runtime_error("Already have a manager and trying to set another"); |
3134 | } |
3135 | |
3136 | - _manager = manager; |
3137 | + g_debug("Setting a new manager"); |
3138 | + reg->impl->manager_ = manager; |
3139 | + |
3140 | + std::call_once(reg->impl->flag_managerSignals, [reg]() { |
3141 | + if (!reg->impl->thread.executeOnThread<bool>([reg]() { |
3142 | + reg->impl->handle_managerSignalFocus = managerSignalHelper( |
3143 | + reg, "UnityFocusRequest", |
3144 | + [](const std::shared_ptr<Registry>& reg, const std::shared_ptr<Application>& app, |
3145 | + const std::shared_ptr<Application::Instance>& instance, |
3146 | + const std::shared_ptr<GDBusConnection>& conn, const std::string& sender, |
3147 | + const std::shared_ptr<GVariant>& params) { |
3148 | + /* Nothing to do today */ |
3149 | + reg->impl->manager_->focusRequest(app, instance, [](bool response) { |
3150 | + /* NOTE: We have no clue what thread this is gonna be |
3151 | + executed on, but since we're just talking to the GDBus |
3152 | + thread it isn't an issue today. Be careful in changing |
3153 | + this code. */ |
3154 | + }); |
3155 | + }); |
3156 | + reg->impl->handle_managerSignalStarting = managerSignalHelper( |
3157 | + reg, "UnityStartingBroadcast", |
3158 | + [](const std::shared_ptr<Registry>& reg, const std::shared_ptr<Application>& app, |
3159 | + const std::shared_ptr<Application::Instance>& instance, |
3160 | + const std::shared_ptr<GDBusConnection>& conn, const std::string& sender, |
3161 | + const std::shared_ptr<GVariant>& params) { |
3162 | + |
3163 | + reg->impl->manager_->startingRequest(app, instance, [conn, sender, params](bool response) { |
3164 | + /* NOTE: We have no clue what thread this is gonna be |
3165 | + executed on, but since we're just talking to the GDBus |
3166 | + thread it isn't an issue today. Be careful in changing |
3167 | + this code. */ |
3168 | + if (response) |
3169 | + { |
3170 | + g_dbus_connection_emit_signal(conn.get(), sender.c_str(), /* destination */ |
3171 | + "/", /* path */ |
3172 | + "com.canonical.UbuntuAppLaunch", /* interface */ |
3173 | + "UnityStartingSignal", /* signal */ |
3174 | + params.get(), /* params, the same */ |
3175 | + nullptr); /* error */ |
3176 | + } |
3177 | + }); |
3178 | + }); |
3179 | + reg->impl->handle_managerSignalResume = managerSignalHelper( |
3180 | + reg, "UnityResumeRequest", |
3181 | + [](const std::shared_ptr<Registry>& reg, const std::shared_ptr<Application>& app, |
3182 | + const std::shared_ptr<Application::Instance>& instance, |
3183 | + const std::shared_ptr<GDBusConnection>& conn, const std::string& sender, |
3184 | + const std::shared_ptr<GVariant>& params) { |
3185 | + reg->impl->manager_->resumeRequest(app, instance, [conn, sender, params](bool response) { |
3186 | + /* NOTE: We have no clue what thread this is gonna be |
3187 | + executed on, but since we're just talking to the GDBus |
3188 | + thread it isn't an issue today. Be careful in changing |
3189 | + this code. */ |
3190 | + if (response) |
3191 | + { |
3192 | + g_dbus_connection_emit_signal(conn.get(), sender.c_str(), /* destination */ |
3193 | + "/", /* path */ |
3194 | + "com.canonical.UbuntuAppLaunch", /* interface */ |
3195 | + "UnityResumeResponse", /* signal */ |
3196 | + params.get(), /* params, the same */ |
3197 | + nullptr); /* error */ |
3198 | + } |
3199 | + }); |
3200 | + }); |
3201 | + |
3202 | + return true; |
3203 | + })) |
3204 | + { |
3205 | + g_warning("Unable to install manager signals"); |
3206 | + } |
3207 | + }); |
3208 | } |
3209 | |
3210 | -void |
3211 | -Registry::Impl::clearManager () |
3212 | +/** Clear the manager pointer */ |
3213 | +void Registry::Impl::clearManager() |
3214 | { |
3215 | - _manager = nullptr; |
3216 | + g_debug("Clearing the manager"); |
3217 | + manager_.reset(); |
3218 | } |
3219 | -#endif |
3220 | |
3221 | /** App start watching, if we're registered for the signal we |
3222 | can't wait on it. We are making this static right now because |
3223 | @@ -576,5 +510,301 @@ |
3224 | return watchingAppStarting_; |
3225 | } |
3226 | |
3227 | +/** Regex to parse the JOB environment variable from Upstart */ |
3228 | +std::regex jobenv_regex{"^JOB=(application\\-(?:click|snap|legacy))$"}; |
3229 | +/** Regex to parse the INSTANCE environment variable from Upstart */ |
3230 | +std::regex instanceenv_regex{"^INSTANCE=(.*?)(?:\\-([0-9]*))?+$"}; |
3231 | + |
3232 | +/** Core of most of the events that come from Upstart directly. Includes parsing of the |
3233 | + Upstart event environment and calling the appropriate signal with the right Application |
3234 | + object and eventually its instance */ |
3235 | +void Registry::Impl::upstartEventEmitted( |
3236 | + core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>>& signal, |
3237 | + std::shared_ptr<GVariant> params, |
3238 | + const std::shared_ptr<Registry>& reg) |
3239 | +{ |
3240 | + std::string jobname; |
3241 | + std::string sappid; |
3242 | + std::string instance; |
3243 | + |
3244 | + gchar* env = nullptr; |
3245 | + GVariant* envs = g_variant_get_child_value(params.get(), 1); |
3246 | + GVariantIter iter; |
3247 | + g_variant_iter_init(&iter, envs); |
3248 | + |
3249 | + while (g_variant_iter_loop(&iter, "s", &env)) |
3250 | + { |
3251 | + std::smatch match; |
3252 | + std::string senv = env; |
3253 | + |
3254 | + if (std::regex_match(senv, match, jobenv_regex)) |
3255 | + { |
3256 | + jobname = match[1].str(); |
3257 | + } |
3258 | + else if (std::regex_match(senv, match, instanceenv_regex)) |
3259 | + { |
3260 | + sappid = match[1].str(); |
3261 | + instance = match[2].str(); |
3262 | + } |
3263 | + } |
3264 | + |
3265 | + g_variant_unref(envs); |
3266 | + |
3267 | + if (jobname.empty()) |
3268 | + { |
3269 | + return; |
3270 | + } |
3271 | + |
3272 | + g_debug("Upstart Event for job '%s' appid '%s' instance '%s'", jobname.c_str(), sappid.c_str(), instance.c_str()); |
3273 | + |
3274 | + auto appid = AppID::find(reg, sappid); |
3275 | + auto app = Application::create(appid, reg); |
3276 | + |
3277 | + // TODO: Figure otu creating instances |
3278 | + |
3279 | + signal(app, {}); |
3280 | +} |
3281 | + |
3282 | +/** Grab the signal object for application startup. If we're not already listing for |
3283 | + those signals this sets up a listener for them. */ |
3284 | +core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>>& Registry::Impl::appStarted( |
3285 | + const std::shared_ptr<Registry>& reg) |
3286 | +{ |
3287 | + std::call_once(flag_appStarted, [reg]() { |
3288 | + reg->impl->thread.executeOnThread<bool>([reg]() { |
3289 | + upstartEventData* data = new upstartEventData{reg}; |
3290 | + |
3291 | + reg->impl->handle_appStarted = g_dbus_connection_signal_subscribe( |
3292 | + reg->impl->_dbus.get(), /* bus */ |
3293 | + nullptr, /* sender */ |
3294 | + DBUS_INTERFACE_UPSTART, /* interface */ |
3295 | + "EventEmitted", /* signal */ |
3296 | + DBUS_PATH_UPSTART, /* path */ |
3297 | + "started", /* arg0 */ |
3298 | + G_DBUS_SIGNAL_FLAGS_NONE, |
3299 | + [](GDBusConnection*, const gchar*, const gchar*, const gchar*, const gchar*, GVariant* params, |
3300 | + gpointer user_data) -> void { |
3301 | + auto data = reinterpret_cast<upstartEventData*>(user_data); |
3302 | + auto reg = data->weakReg.lock(); |
3303 | + auto sparams = std::shared_ptr<GVariant>(g_variant_ref(params), g_variant_unref); |
3304 | + reg->impl->upstartEventEmitted(reg->impl->sig_appStarted, sparams, reg); |
3305 | + }, /* callback */ |
3306 | + data, /* user data */ |
3307 | + [](gpointer user_data) { |
3308 | + auto data = reinterpret_cast<upstartEventData*>(user_data); |
3309 | + delete data; |
3310 | + }); /* user data destroy */ |
3311 | + |
3312 | + return true; |
3313 | + }); |
3314 | + }); |
3315 | + |
3316 | + return sig_appStarted; |
3317 | +} |
3318 | + |
3319 | +/** Grab the signal object for application stopping. If we're not already listing for |
3320 | + those signals this sets up a listener for them. */ |
3321 | +core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>>& Registry::Impl::appStopped( |
3322 | + const std::shared_ptr<Registry>& reg) |
3323 | +{ |
3324 | + std::call_once(flag_appStopped, [reg]() { |
3325 | + reg->impl->thread.executeOnThread<bool>([reg]() { |
3326 | + upstartEventData* data = new upstartEventData{reg}; |
3327 | + |
3328 | + reg->impl->handle_appStopped = g_dbus_connection_signal_subscribe( |
3329 | + reg->impl->_dbus.get(), /* bus */ |
3330 | + nullptr, /* sender */ |
3331 | + DBUS_INTERFACE_UPSTART, /* interface */ |
3332 | + "EventEmitted", /* signal */ |
3333 | + DBUS_PATH_UPSTART, /* path */ |
3334 | + "stopped", /* arg0 */ |
3335 | + G_DBUS_SIGNAL_FLAGS_NONE, |
3336 | + [](GDBusConnection*, const gchar*, const gchar*, const gchar*, const gchar*, GVariant* params, |
3337 | + gpointer user_data) -> void { |
3338 | + auto data = reinterpret_cast<upstartEventData*>(user_data); |
3339 | + auto reg = data->weakReg.lock(); |
3340 | + auto sparams = std::shared_ptr<GVariant>(g_variant_ref(params), g_variant_unref); |
3341 | + reg->impl->upstartEventEmitted(reg->impl->sig_appStopped, sparams, reg); |
3342 | + }, /* callback */ |
3343 | + data, /* user data */ |
3344 | + [](gpointer user_data) { |
3345 | + auto data = reinterpret_cast<upstartEventData*>(user_data); |
3346 | + delete data; |
3347 | + }); /* user data destroy */ |
3348 | + |
3349 | + return true; |
3350 | + }); |
3351 | + }); |
3352 | + |
3353 | + return sig_appStopped; |
3354 | +} |
3355 | + |
3356 | +/** Grab the signal object for application failing. If we're not already listing for |
3357 | + those signals this sets up a listener for them. */ |
3358 | +core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, Registry::FailureType>& |
3359 | + Registry::Impl::appFailed(const std::shared_ptr<Registry>& reg) |
3360 | +{ |
3361 | + std::call_once(flag_appFailed, [reg]() { |
3362 | + reg->impl->thread.executeOnThread<bool>([reg]() { |
3363 | + upstartEventData* data = new upstartEventData{reg}; |
3364 | + |
3365 | + reg->impl->handle_appFailed = g_dbus_connection_signal_subscribe( |
3366 | + reg->impl->_dbus.get(), /* bus */ |
3367 | + nullptr, /* sender */ |
3368 | + "com.canonical.UbuntuAppLaunch", /* interface */ |
3369 | + "ApplicationFailed", /* signal */ |
3370 | + "/", /* path */ |
3371 | + nullptr, /* arg0 */ |
3372 | + G_DBUS_SIGNAL_FLAGS_NONE, |
3373 | + [](GDBusConnection*, const gchar*, const gchar*, const gchar*, const gchar*, GVariant* params, |
3374 | + gpointer user_data) -> void { |
3375 | + auto data = reinterpret_cast<upstartEventData*>(user_data); |
3376 | + auto reg = data->weakReg.lock(); |
3377 | + |
3378 | + const gchar* sappid = NULL; |
3379 | + const gchar* typestr = NULL; |
3380 | + |
3381 | + Registry::FailureType type = Registry::FailureType::CRASH; |
3382 | + g_variant_get(params, "(&s&s)", &sappid, &typestr); |
3383 | + |
3384 | + if (g_strcmp0("crash", typestr) == 0) |
3385 | + { |
3386 | + type = Registry::FailureType::CRASH; |
3387 | + } |
3388 | + else if (g_strcmp0("start-failure", typestr) == 0) |
3389 | + { |
3390 | + type = Registry::FailureType::START_FAILURE; |
3391 | + } |
3392 | + else |
3393 | + { |
3394 | + g_warning("Application failure type '%s' unknown, reporting as a crash", typestr); |
3395 | + } |
3396 | + |
3397 | + auto appid = AppID::find(reg, sappid); |
3398 | + auto app = Application::create(appid, reg); |
3399 | + |
3400 | + /* TODO: Instance issues */ |
3401 | + |
3402 | + reg->impl->sig_appFailed(app, {}, type); |
3403 | + }, /* callback */ |
3404 | + data, /* user data */ |
3405 | + [](gpointer user_data) { |
3406 | + auto data = reinterpret_cast<upstartEventData*>(user_data); |
3407 | + delete data; |
3408 | + }); /* user data destroy */ |
3409 | + |
3410 | + return true; |
3411 | + }); |
3412 | + }); |
3413 | + |
3414 | + return sig_appFailed; |
3415 | +} |
3416 | + |
3417 | +/** Core handler for pause and resume events. Includes turning the GVariant |
3418 | + pid list into a std::vector and getting the application object. */ |
3419 | +void Registry::Impl::pauseEventEmitted( |
3420 | + core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, std::vector<pid_t>&>& signal, |
3421 | + const std::shared_ptr<GVariant>& params, |
3422 | + const std::shared_ptr<Registry>& reg) |
3423 | +{ |
3424 | + std::vector<pid_t> pids; |
3425 | + GVariant* vappid = g_variant_get_child_value(params.get(), 0); |
3426 | + GVariant* vpids = g_variant_get_child_value(params.get(), 1); |
3427 | + guint64 pid; |
3428 | + GVariantIter thispid; |
3429 | + g_variant_iter_init(&thispid, vpids); |
3430 | + |
3431 | + while (g_variant_iter_loop(&thispid, "t", &pid)) |
3432 | + { |
3433 | + pids.emplace_back(pid); |
3434 | + } |
3435 | + |
3436 | + auto cappid = g_variant_get_string(vappid, NULL); |
3437 | + auto appid = ubuntu::app_launch::AppID::find(reg, cappid); |
3438 | + auto app = Application::create(appid, reg); |
3439 | + |
3440 | + /* TODO: Instance */ |
3441 | + signal(app, {}, pids); |
3442 | + |
3443 | + g_variant_unref(vappid); |
3444 | + g_variant_unref(vpids); |
3445 | + |
3446 | + return; |
3447 | +} |
3448 | + |
3449 | +/** Grab the signal object for application paused. If we're not already listing for |
3450 | + those signals this sets up a listener for them. */ |
3451 | +core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, std::vector<pid_t>&>& |
3452 | + Registry::Impl::appPaused(const std::shared_ptr<Registry>& reg) |
3453 | +{ |
3454 | + std::call_once(flag_appPaused, [&]() { |
3455 | + reg->impl->thread.executeOnThread<bool>([reg]() { |
3456 | + upstartEventData* data = new upstartEventData{reg}; |
3457 | + |
3458 | + reg->impl->handle_appPaused = g_dbus_connection_signal_subscribe( |
3459 | + reg->impl->_dbus.get(), /* bus */ |
3460 | + nullptr, /* sender */ |
3461 | + "com.canonical.UbuntuAppLaunch", /* interface */ |
3462 | + "ApplicationPaused", /* signal */ |
3463 | + "/", /* path */ |
3464 | + nullptr, /* arg0 */ |
3465 | + G_DBUS_SIGNAL_FLAGS_NONE, |
3466 | + [](GDBusConnection*, const gchar*, const gchar*, const gchar*, const gchar*, GVariant* params, |
3467 | + gpointer user_data) -> void { |
3468 | + auto data = reinterpret_cast<upstartEventData*>(user_data); |
3469 | + auto reg = data->weakReg.lock(); |
3470 | + auto sparams = std::shared_ptr<GVariant>(g_variant_ref(params), g_variant_unref); |
3471 | + reg->impl->pauseEventEmitted(reg->impl->sig_appPaused, sparams, reg); |
3472 | + }, /* callback */ |
3473 | + data, /* user data */ |
3474 | + [](gpointer user_data) { |
3475 | + auto data = reinterpret_cast<upstartEventData*>(user_data); |
3476 | + delete data; |
3477 | + }); /* user data destroy */ |
3478 | + |
3479 | + return true; |
3480 | + }); |
3481 | + }); |
3482 | + |
3483 | + return sig_appPaused; |
3484 | +} |
3485 | + |
3486 | +/** Grab the signal object for application resumed. If we're not already listing for |
3487 | + those signals this sets up a listener for them. */ |
3488 | +core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, std::vector<pid_t>&>& |
3489 | + Registry::Impl::appResumed(const std::shared_ptr<Registry>& reg) |
3490 | +{ |
3491 | + std::call_once(flag_appResumed, [&]() { |
3492 | + reg->impl->thread.executeOnThread<bool>([reg]() { |
3493 | + upstartEventData* data = new upstartEventData{reg}; |
3494 | + |
3495 | + reg->impl->handle_appResumed = g_dbus_connection_signal_subscribe( |
3496 | + reg->impl->_dbus.get(), /* bus */ |
3497 | + nullptr, /* sender */ |
3498 | + "com.canonical.UbuntuAppLaunch", /* interface */ |
3499 | + "ApplicationResumed", /* signal */ |
3500 | + "/", /* path */ |
3501 | + nullptr, /* arg0 */ |
3502 | + G_DBUS_SIGNAL_FLAGS_NONE, |
3503 | + [](GDBusConnection*, const gchar*, const gchar*, const gchar*, const gchar*, GVariant* params, |
3504 | + gpointer user_data) -> void { |
3505 | + auto data = reinterpret_cast<upstartEventData*>(user_data); |
3506 | + auto reg = data->weakReg.lock(); |
3507 | + auto sparams = std::shared_ptr<GVariant>(g_variant_ref(params), g_variant_unref); |
3508 | + reg->impl->pauseEventEmitted(reg->impl->sig_appResumed, sparams, reg); |
3509 | + }, /* callback */ |
3510 | + data, /* user data */ |
3511 | + [](gpointer user_data) { |
3512 | + auto data = reinterpret_cast<upstartEventData*>(user_data); |
3513 | + delete data; |
3514 | + }); /* user data destroy */ |
3515 | + |
3516 | + return true; |
3517 | + }); |
3518 | + }); |
3519 | + |
3520 | + return sig_appResumed; |
3521 | +} |
3522 | + |
3523 | } // namespace app_launch |
3524 | } // namespace ubuntu |
3525 | |
3526 | === modified file 'libubuntu-app-launch/registry-impl.h' |
3527 | --- libubuntu-app-launch/registry-impl.h 2016-09-23 22:30:51 +0000 |
3528 | +++ libubuntu-app-launch/registry-impl.h 2016-11-10 21:58:30 +0000 |
3529 | @@ -18,6 +18,7 @@ |
3530 | */ |
3531 | |
3532 | #include "glib-thread.h" |
3533 | +#include "jobs-base.h" |
3534 | #include "registry.h" |
3535 | #include "snapd-info.h" |
3536 | #include <click.h> |
3537 | @@ -53,10 +54,8 @@ |
3538 | std::list<AppID::Package> getClickPackages(); |
3539 | std::string getClickDir(const std::string& package); |
3540 | |
3541 | -#if 0 |
3542 | - void setManager (Registry::Manager* manager); |
3543 | - void clearManager (); |
3544 | -#endif |
3545 | + static void setManager(std::shared_ptr<Registry::Manager> manager, std::shared_ptr<Registry> registry); |
3546 | + void clearManager(); |
3547 | |
3548 | /** Shared context thread for events and background tasks |
3549 | that UAL subtasks are doing */ |
3550 | @@ -69,19 +68,27 @@ |
3551 | snapd::Info snapdInfo; |
3552 | #endif |
3553 | |
3554 | + std::shared_ptr<jobs::manager::Base> jobs; |
3555 | + |
3556 | std::shared_ptr<IconFinder> getIconFinder(std::string basePath); |
3557 | |
3558 | void zgSendEvent(AppID appid, const std::string& eventtype); |
3559 | |
3560 | - std::vector<pid_t> pidsFromCgroup(const std::string& jobpath); |
3561 | - |
3562 | - /* Upstart Jobs */ |
3563 | - std::list<std::string> upstartInstancesForJob(const std::string& job); |
3564 | - std::string upstartJobPath(const std::string& job); |
3565 | - |
3566 | static std::string printJson(std::shared_ptr<JsonObject> jsonobj); |
3567 | static std::string printJson(std::shared_ptr<JsonNode> jsonnode); |
3568 | |
3569 | + /* Signals to discover what is happening to apps */ |
3570 | + core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>>& appStarted( |
3571 | + const std::shared_ptr<Registry>& reg); |
3572 | + core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>>& appStopped( |
3573 | + const std::shared_ptr<Registry>& reg); |
3574 | + core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, FailureType>& appFailed( |
3575 | + const std::shared_ptr<Registry>& reg); |
3576 | + core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, std::vector<pid_t>&>& appPaused( |
3577 | + const std::shared_ptr<Registry>& reg); |
3578 | + core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, std::vector<pid_t>&>& appResumed( |
3579 | + const std::shared_ptr<Registry>& reg); |
3580 | + |
3581 | /* Signal Hints */ |
3582 | /* NOTE: Static because we don't have registry instances in the C |
3583 | code right now. We want these to not be static in the future */ |
3584 | @@ -89,27 +96,73 @@ |
3585 | static bool isWatchingAppStarting(); |
3586 | |
3587 | private: |
3588 | - Registry* _registry; |
3589 | -#if 0 |
3590 | - Registry::Manager* _manager; |
3591 | -#endif |
3592 | - |
3593 | - std::shared_ptr<ClickDB> _clickDB; |
3594 | - std::shared_ptr<ClickUser> _clickUser; |
3595 | + Registry* _registry; /**< The Registry that we're spawned from */ |
3596 | + std::shared_ptr<Registry::Manager> manager_; /**< Application manager if registered */ |
3597 | + |
3598 | + std::shared_ptr<ClickDB> _clickDB; /**< Shared instance of the Click Database */ |
3599 | + std::shared_ptr<ClickUser> _clickUser; /**< Click database filtered by the current user */ |
3600 | + |
3601 | + /** Signal object for applications started */ |
3602 | + core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>> sig_appStarted; |
3603 | + /** Signal object for applications stopped */ |
3604 | + core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>> sig_appStopped; |
3605 | + /** Signal object for applications failed */ |
3606 | + core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, FailureType> sig_appFailed; |
3607 | + /** Signal object for applications paused */ |
3608 | + core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, std::vector<pid_t>&> |
3609 | + sig_appPaused; |
3610 | + /** Signal object for applications resumed */ |
3611 | + core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, std::vector<pid_t>&> |
3612 | + sig_appResumed; |
3613 | + |
3614 | + guint handle_appStarted{0}; /**< GDBus signal watcher handle for app started signal */ |
3615 | + guint handle_appStopped{0}; /**< GDBus signal watcher handle for app stopped signal */ |
3616 | + guint handle_appFailed{0}; /**< GDBus signal watcher handle for app failed signal */ |
3617 | + guint handle_appPaused{0}; /**< GDBus signal watcher handle for app paused signal */ |
3618 | + guint handle_appResumed{0}; /**< GDBus signal watcher handle for app resumed signal */ |
3619 | + guint handle_managerSignalFocus{0}; /**< GDBus signal watcher handle for app focused signal */ |
3620 | + guint handle_managerSignalResume{0}; /**< GDBus signal watcher handle for app resumed signal */ |
3621 | + guint handle_managerSignalStarting{0}; /**< GDBus signal watcher handle for app starting signal */ |
3622 | + |
3623 | + std::once_flag flag_appStarted; /**< Variable to track to see if signal handlers are installed for application |
3624 | + started */ |
3625 | + std::once_flag flag_appStopped; /**< Variable to track to see if signal handlers are installed for application |
3626 | + stopped */ |
3627 | + std::once_flag |
3628 | + flag_appFailed; /**< Variable to track to see if signal handlers are installed for application failed */ |
3629 | + std::once_flag |
3630 | + flag_appPaused; /**< Variable to track to see if signal handlers are installed for application paused */ |
3631 | + std::once_flag flag_appResumed; /**< Variable to track to see if signal handlers are installed for application |
3632 | + resumed */ |
3633 | + std::once_flag flag_managerSignals; /**< Variable to track to see if signal handlers are installed for the manager |
3634 | + signals of focused, resumed and starting */ |
3635 | + |
3636 | + void upstartEventEmitted(core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>>& signal, |
3637 | + std::shared_ptr<GVariant> params, |
3638 | + const std::shared_ptr<Registry>& reg); |
3639 | + void pauseEventEmitted( |
3640 | + core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, std::vector<pid_t>&>& signal, |
3641 | + const std::shared_ptr<GVariant>& params, |
3642 | + const std::shared_ptr<Registry>& reg); |
3643 | + static std::tuple<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>> managerParams( |
3644 | + const std::shared_ptr<GVariant>& params, const std::shared_ptr<Registry>& reg); |
3645 | + static guint managerSignalHelper(const std::shared_ptr<Registry>& reg, |
3646 | + const std::string& signalname, |
3647 | + std::function<void(const std::shared_ptr<Registry>& reg, |
3648 | + const std::shared_ptr<Application>& app, |
3649 | + const std::shared_ptr<Application::Instance>& instance, |
3650 | + const std::shared_ptr<GDBusConnection>&, |
3651 | + const std::string&, |
3652 | + const std::shared_ptr<GVariant>&)> responsefunc); |
3653 | |
3654 | void initClick(); |
3655 | |
3656 | + /** Shared instance of the Zeitgeist Log */ |
3657 | std::shared_ptr<ZeitgeistLog> zgLog_; |
3658 | |
3659 | - std::shared_ptr<GDBusConnection> cgManager_; |
3660 | - |
3661 | - void initCGManager(); |
3662 | - |
3663 | + /** All of our icon finders based on the path that they're looking |
3664 | + into */ |
3665 | std::unordered_map<std::string, std::shared_ptr<IconFinder>> _iconFinders; |
3666 | - |
3667 | - /** Getting the Upstart job path is relatively expensive in |
3668 | - that it requires a DBus call. Worth keeping a cache of. */ |
3669 | - std::map<std::string, std::string> upstartJobPathCache_; |
3670 | }; |
3671 | |
3672 | } // namespace app_launch |
3673 | |
3674 | === modified file 'libubuntu-app-launch/registry.cpp' |
3675 | --- libubuntu-app-launch/registry.cpp 2016-08-26 17:33:34 +0000 |
3676 | +++ libubuntu-app-launch/registry.cpp 2016-11-10 21:58:30 +0000 |
3677 | @@ -47,61 +47,14 @@ |
3678 | { |
3679 | } |
3680 | |
3681 | -std::list<std::shared_ptr<Application>> Registry::runningApps(std::shared_ptr<Registry> connection) |
3682 | +std::list<std::shared_ptr<Application>> Registry::runningApps(std::shared_ptr<Registry> registry) |
3683 | { |
3684 | - std::list<std::string> instances; |
3685 | - |
3686 | - /* Get all the legacy instances */ |
3687 | - instances.splice(instances.begin(), connection->impl->upstartInstancesForJob("application-legacy")); |
3688 | - /* Get all the snap instances */ |
3689 | - instances.splice(instances.begin(), connection->impl->upstartInstancesForJob("application-snap")); |
3690 | - |
3691 | - /* Remove the instance ID */ |
3692 | - std::transform(instances.begin(), instances.end(), instances.begin(), [](std::string &instancename) -> std::string { |
3693 | - static const std::regex instanceregex("^(.*)-[0-9]*$"); |
3694 | - std::smatch match; |
3695 | - if (std::regex_match(instancename, match, instanceregex)) |
3696 | - { |
3697 | - return match[1].str(); |
3698 | - } |
3699 | - else |
3700 | - { |
3701 | - g_warning("Unable to match instance name: %s", instancename.c_str()); |
3702 | - return {}; |
3703 | - } |
3704 | - }); |
3705 | - |
3706 | - /* Deduplicate Set */ |
3707 | - std::set<std::string> instanceset; |
3708 | - for (auto instance : instances) |
3709 | - { |
3710 | - if (!instance.empty()) |
3711 | - instanceset.insert(instance); |
3712 | - } |
3713 | - |
3714 | - /* Add in the click instances */ |
3715 | - for (auto instance : connection->impl->upstartInstancesForJob("application-click")) |
3716 | - { |
3717 | - instanceset.insert(instance); |
3718 | - } |
3719 | - |
3720 | - g_debug("Overall there are %d instances: %s", int(instanceset.size()), |
3721 | - std::accumulate(instanceset.begin(), instanceset.end(), std::string{}, |
3722 | - [](const std::string &instr, std::string instance) { |
3723 | - return instr.empty() ? instance : instr + ", " + instance; |
3724 | - }) |
3725 | - .c_str()); |
3726 | - |
3727 | - /* Convert to Applications */ |
3728 | - std::list<std::shared_ptr<Application>> apps; |
3729 | - for (auto instance : instanceset) |
3730 | - { |
3731 | - auto appid = AppID::find(connection, instance); |
3732 | - auto app = Application::create(appid, connection); |
3733 | - apps.push_back(app); |
3734 | - } |
3735 | - |
3736 | - return apps; |
3737 | + if (!registry->impl->jobs) |
3738 | + { |
3739 | + registry->impl->jobs = jobs::manager::Base::determineFactory(registry); |
3740 | + } |
3741 | + |
3742 | + return registry->impl->jobs->runningApps(); |
3743 | } |
3744 | |
3745 | std::list<std::shared_ptr<Application>> Registry::installedApps(std::shared_ptr<Registry> connection) |
3746 | @@ -127,6 +80,16 @@ |
3747 | return list; |
3748 | } |
3749 | |
3750 | +void Registry::setManager(std::shared_ptr<Manager> manager, std::shared_ptr<Registry> registry) |
3751 | +{ |
3752 | + Registry::Impl::setManager(manager, registry); |
3753 | +} |
3754 | + |
3755 | +void Registry::clearManager() |
3756 | +{ |
3757 | + impl->clearManager(); |
3758 | +} |
3759 | + |
3760 | std::shared_ptr<Registry> defaultRegistry; |
3761 | std::shared_ptr<Registry> Registry::getDefault() |
3762 | { |
3763 | @@ -143,5 +106,35 @@ |
3764 | defaultRegistry.reset(); |
3765 | } |
3766 | |
3767 | +core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>>& Registry::appStarted( |
3768 | + const std::shared_ptr<Registry>& reg) |
3769 | +{ |
3770 | + return reg->impl->appStarted(reg); |
3771 | +} |
3772 | + |
3773 | +core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>>& Registry::appStopped( |
3774 | + const std::shared_ptr<Registry>& reg) |
3775 | +{ |
3776 | + return reg->impl->appStopped(reg); |
3777 | +} |
3778 | + |
3779 | +core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, Registry::FailureType>& |
3780 | + Registry::appFailed(const std::shared_ptr<Registry>& reg) |
3781 | +{ |
3782 | + return reg->impl->appFailed(reg); |
3783 | +} |
3784 | + |
3785 | +core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, std::vector<pid_t>&>& |
3786 | + Registry::appPaused(const std::shared_ptr<Registry>& reg) |
3787 | +{ |
3788 | + return reg->impl->appPaused(reg); |
3789 | +} |
3790 | + |
3791 | +core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, std::vector<pid_t>&>& |
3792 | + Registry::appResumed(const std::shared_ptr<Registry>& reg) |
3793 | +{ |
3794 | + return reg->impl->appResumed(reg); |
3795 | +} |
3796 | + |
3797 | } // namespace app_launch |
3798 | } // namespace ubuntu |
3799 | |
3800 | === modified file 'libubuntu-app-launch/registry.h' |
3801 | --- libubuntu-app-launch/registry.h 2016-06-09 14:55:34 +0000 |
3802 | +++ libubuntu-app-launch/registry.h 2016-11-10 21:58:30 +0000 |
3803 | @@ -73,28 +73,134 @@ |
3804 | */ |
3805 | static std::list<std::shared_ptr<Application>> installedApps(std::shared_ptr<Registry> registry = getDefault()); |
3806 | |
3807 | -#if 0 /* TODO -- In next MR */ |
3808 | /* Signals to discover what is happening to apps */ |
3809 | - core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>> appStarted; |
3810 | - core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>> appStopped; |
3811 | - core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, FailureType> appFailed; |
3812 | - core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>> appPaused; |
3813 | - core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>> appResumed; |
3814 | - |
3815 | - /* The Application Manager, almost always if you're not Unity8, don't |
3816 | - use this API. Testing is a special case. */ |
3817 | + /** Get the signal object that is signaled when an application has been |
3818 | + started. |
3819 | + |
3820 | + \note This singal handler is activated on the UAL thread, if you want |
3821 | + to execute on a different thread you'll need to move the work. |
3822 | + |
3823 | + \param reg Registry to get the handler from |
3824 | + */ |
3825 | + static core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>>& appStarted( |
3826 | + const std::shared_ptr<Registry>& reg = getDefault()); |
3827 | + |
3828 | + /** Get the signal object that is signaled when an application has stopped. |
3829 | + |
3830 | + \note This singal handler is activated on the UAL thread, if you want |
3831 | + to execute on a different thread you'll need to move the work. |
3832 | + |
3833 | + \param reg Registry to get the handler from |
3834 | + */ |
3835 | + static core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>>& appStopped( |
3836 | + const std::shared_ptr<Registry>& reg = getDefault()); |
3837 | + |
3838 | + /** Get the signal object that is signaled when an application has failed. |
3839 | + |
3840 | + \note This singal handler is activated on the UAL thread, if you want |
3841 | + to execute on a different thread you'll need to move the work. |
3842 | + |
3843 | + \param reg Registry to get the handler from |
3844 | + */ |
3845 | + static core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, FailureType>& appFailed( |
3846 | + const std::shared_ptr<Registry>& reg = getDefault()); |
3847 | + |
3848 | + /** Get the signal object that is signaled when an application has been |
3849 | + paused. |
3850 | + |
3851 | + \note This singal handler is activated on the UAL thread, if you want |
3852 | + to execute on a different thread you'll need to move the work. |
3853 | + |
3854 | + \param reg Registry to get the handler from |
3855 | + */ |
3856 | + static core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, std::vector<pid_t>&>& |
3857 | + appPaused(const std::shared_ptr<Registry>& reg = getDefault()); |
3858 | + |
3859 | + /** Get the signal object that is signaled when an application has been |
3860 | + resumed. |
3861 | + |
3862 | + \note This singal handler is activated on the UAL thread, if you want |
3863 | + to execute on a different thread you'll need to move the work. |
3864 | + |
3865 | + \param reg Registry to get the handler from |
3866 | + */ |
3867 | + static core::Signal<std::shared_ptr<Application>, std::shared_ptr<Application::Instance>, std::vector<pid_t>&>& |
3868 | + appResumed(const std::shared_ptr<Registry>& reg = getDefault()); |
3869 | + |
3870 | + /** The Application Manager, almost always if you're not Unity8, don't |
3871 | + use this API. Testing is a special case. Subclass this interface and |
3872 | + implement these functions. |
3873 | + |
3874 | + Each function here is being passed a function object that takes a boolean |
3875 | + to reply. This will accept or reject the request. The function object |
3876 | + can be copied to another thread and executed if needed. |
3877 | + */ |
3878 | class Manager |
3879 | { |
3880 | - virtual bool focusRequest (std::shared_ptr<Application> app, std::shared_ptr<Application::Instance> instance) = 0; |
3881 | - virtual bool startingRequest (std::shared_ptr<Application> app, std::shared_ptr<Application::Instance> instance) = 0; |
3882 | + public: |
3883 | + /** Application wishes to startup |
3884 | + |
3885 | + \note This singal handler is activated on the UAL thread, if you want |
3886 | + to execute on a different thread you'll need to move the work. |
3887 | + |
3888 | + \param app Application requesting startup |
3889 | + \param instance Instance of the app, always valid but not useful |
3890 | + unless mulit-instance app. |
3891 | + \param reply Function object to reply if it is allowed to start |
3892 | + */ |
3893 | + virtual void startingRequest(std::shared_ptr<Application> app, |
3894 | + std::shared_ptr<Application::Instance> instance, |
3895 | + std::function<void(bool)> reply) = 0; |
3896 | + |
3897 | + /** Application wishes to have focus. Usually this occurs when |
3898 | + a URL for the application is activated and the running app is |
3899 | + requested. |
3900 | + |
3901 | + \note This singal handler is activated on the UAL thread, if you want |
3902 | + to execute on a different thread you'll need to move the work. |
3903 | + |
3904 | + \param app Application requesting focus |
3905 | + \param instance Instance of the app, always valid but not useful |
3906 | + unless mulit-instance app. |
3907 | + \param reply Function object to reply if it is allowed to focus |
3908 | + */ |
3909 | + virtual void focusRequest(std::shared_ptr<Application> app, |
3910 | + std::shared_ptr<Application::Instance> instance, |
3911 | + std::function<void(bool)> reply) = 0; |
3912 | + |
3913 | + /** Application wishes to resume. Usually this occurs when |
3914 | + a URL for the application is activated and the running app is |
3915 | + requested. |
3916 | + |
3917 | + \note This singal handler is activated on the UAL thread, if you want |
3918 | + to execute on a different thread you'll need to move the work. |
3919 | + |
3920 | + \param app Application requesting resume |
3921 | + \param instance Instance of the app, always valid but not useful |
3922 | + unless mulit-instance app. |
3923 | + \param reply Function object to reply if it is allowed to resume |
3924 | + */ |
3925 | + virtual void resumeRequest(std::shared_ptr<Application> app, |
3926 | + std::shared_ptr<Application::Instance> instance, |
3927 | + std::function<void(bool)> reply) = 0; |
3928 | |
3929 | protected: |
3930 | Manager() = default; |
3931 | }; |
3932 | |
3933 | - void setManager (Manager* manager); |
3934 | - void clearManager (); |
3935 | -#endif |
3936 | + /** Set the manager of applications, which gives permissions for them to |
3937 | + start and gain focus. In almost all cases this should be Unity8 as it |
3938 | + will be controlling applications. |
3939 | + |
3940 | + This function will failure if there is already a manager set. |
3941 | + |
3942 | + \param manager A reference to the Manager object to call |
3943 | + \param registry Registry to register the manager on |
3944 | + */ |
3945 | + static void setManager(std::shared_ptr<Manager> manager, std::shared_ptr<Registry> registry); |
3946 | + |
3947 | + /** Remove the current manager on the registry */ |
3948 | + void clearManager(); |
3949 | |
3950 | /* Helper Lists */ |
3951 | /** Get a list of all the helpers for a given helper type |
3952 | |
3953 | === modified file 'libubuntu-app-launch/ubuntu-app-launch.cpp' |
3954 | --- libubuntu-app-launch/ubuntu-app-launch.cpp 2016-09-14 21:58:39 +0000 |
3955 | +++ libubuntu-app-launch/ubuntu-app-launch.cpp 2016-11-10 21:58:30 +0000 |
3956 | @@ -39,6 +39,7 @@ |
3957 | #include "appid.h" |
3958 | #include "registry.h" |
3959 | #include "registry-impl.h" |
3960 | +#include <algorithm> |
3961 | |
3962 | static void free_helper (gpointer value); |
3963 | int kill (pid_t pid, int signal) noexcept; |
3964 | @@ -271,155 +272,210 @@ |
3965 | gpointer user_data; |
3966 | }; |
3967 | |
3968 | -/* The data we keep for each failed observer */ |
3969 | -typedef struct _paused_resumed_observer_t paused_resumed_observer_t; |
3970 | -struct _paused_resumed_observer_t { |
3971 | - GDBusConnection * conn; |
3972 | - guint sighandle; |
3973 | - UbuntuAppLaunchAppPausedResumedObserver func; |
3974 | - gpointer user_data; |
3975 | - const gchar * lttng_signal; |
3976 | -}; |
3977 | - |
3978 | -/* The lists of Observers */ |
3979 | -static GList * starting_array = NULL; |
3980 | -static GList * started_array = NULL; |
3981 | -static GList * stop_array = NULL; |
3982 | -static GList * focus_array = NULL; |
3983 | -static GList * resume_array = NULL; |
3984 | -static GList * failed_array = NULL; |
3985 | -static GList * paused_array = NULL; |
3986 | -static GList * resumed_array = NULL; |
3987 | - |
3988 | -static void |
3989 | -observer_cb (GDBusConnection * conn, const gchar * sender, const gchar * object, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data) |
3990 | -{ |
3991 | - observer_t * observer = (observer_t *)user_data; |
3992 | - |
3993 | - const gchar * signalname = NULL; |
3994 | - g_variant_get_child(params, 0, "&s", &signalname); |
3995 | - |
3996 | - ual_tracepoint(observer_start, signalname); |
3997 | - |
3998 | - gchar * env = NULL; |
3999 | - GVariant * envs = g_variant_get_child_value(params, 1); |
4000 | - GVariantIter iter; |
4001 | - g_variant_iter_init(&iter, envs); |
4002 | - |
4003 | - gboolean job_found = FALSE; |
4004 | - gboolean job_legacy = FALSE; |
4005 | - gchar * instance = NULL; |
4006 | - |
4007 | - while (g_variant_iter_loop(&iter, "s", &env)) { |
4008 | - if (g_strcmp0(env, "JOB=application-click") == 0) { |
4009 | - job_found = TRUE; |
4010 | - } else if (g_strcmp0(env, "JOB=application-legacy") == 0) { |
4011 | - job_found = TRUE; |
4012 | - job_legacy = TRUE; |
4013 | - } else if (g_strcmp0(env, "JOB=application-snap") == 0) { |
4014 | - job_found = TRUE; |
4015 | - job_legacy = TRUE; |
4016 | - } else if (g_str_has_prefix(env, "INSTANCE=")) { |
4017 | - instance = g_strdup(env + strlen("INSTANCE=")); |
4018 | - } |
4019 | - } |
4020 | - |
4021 | - g_variant_unref(envs); |
4022 | - |
4023 | - if (job_legacy && instance != NULL) { |
4024 | - gchar * dash = g_strrstr(instance, "-"); |
4025 | - if (dash != NULL) { |
4026 | - dash[0] = '\0'; |
4027 | - } |
4028 | - } |
4029 | - |
4030 | - if (job_found && instance != NULL) { |
4031 | - observer->func(instance, observer->user_data); |
4032 | - } |
4033 | - |
4034 | - ual_tracepoint(observer_finish, signalname); |
4035 | - |
4036 | - g_free(instance); |
4037 | -} |
4038 | - |
4039 | -/* Creates the observer structure and registers for the signal with |
4040 | - GDBus so that we can get a callback */ |
4041 | -static gboolean |
4042 | -add_app_generic (UbuntuAppLaunchAppObserver observer, gpointer user_data, const gchar * signal, GList ** list) |
4043 | -{ |
4044 | - GDBusConnection * conn = gdbus_upstart_ref(); |
4045 | - |
4046 | - if (conn == NULL) { |
4047 | - return FALSE; |
4048 | - } |
4049 | - |
4050 | - observer_t * observert = g_new0(observer_t, 1); |
4051 | - |
4052 | - observert->conn = conn; |
4053 | - observert->func = observer; |
4054 | - observert->user_data = user_data; |
4055 | - |
4056 | - *list = g_list_prepend(*list, observert); |
4057 | - |
4058 | - observert->sighandle = g_dbus_connection_signal_subscribe(conn, |
4059 | - NULL, /* sender */ |
4060 | - DBUS_INTERFACE_UPSTART, /* interface */ |
4061 | - "EventEmitted", /* signal */ |
4062 | - DBUS_PATH_UPSTART, /* path */ |
4063 | - signal, /* arg0 */ |
4064 | - G_DBUS_SIGNAL_FLAGS_NONE, |
4065 | - observer_cb, |
4066 | - observert, |
4067 | - NULL); /* user data destroy */ |
4068 | - |
4069 | - return TRUE; |
4070 | -} |
4071 | +/* Function to take a work function and have it execute on a given |
4072 | + GMainContext */ |
4073 | +static void executeOnContext (std::shared_ptr<GMainContext> context, std::function<void()> work) |
4074 | +{ |
4075 | + if (!context) { |
4076 | + work(); |
4077 | + return; |
4078 | + } |
4079 | + |
4080 | + auto heapWork = new std::function<void()>(work); |
4081 | + |
4082 | + auto source = std::shared_ptr<GSource>(g_idle_source_new(), [](GSource* src) { g_clear_pointer(&src, g_source_unref); }); |
4083 | + g_source_set_callback(source.get(), |
4084 | + [](gpointer data) { |
4085 | + auto heapWork = static_cast<std::function<void()>*>(data); |
4086 | + (*heapWork)(); |
4087 | + return G_SOURCE_REMOVE; |
4088 | + }, |
4089 | + heapWork, |
4090 | + [](gpointer data) { |
4091 | + auto heapWork = static_cast<std::function<void()>*>(data); |
4092 | + delete heapWork; |
4093 | + }); |
4094 | + |
4095 | + g_source_attach(source.get(), context.get()); |
4096 | +} |
4097 | + |
4098 | +/* Map of all the observers listening for app started */ |
4099 | +static std::map<std::pair<UbuntuAppLaunchAppObserver, gpointer>, core::ScopedConnection> appStartedObservers; |
4100 | |
4101 | gboolean |
4102 | ubuntu_app_launch_observer_add_app_started (UbuntuAppLaunchAppObserver observer, gpointer user_data) |
4103 | { |
4104 | - return add_app_generic(observer, user_data, "started", &started_array); |
4105 | -} |
4106 | + auto context = std::shared_ptr<GMainContext>(g_main_context_ref_thread_default(), [](GMainContext * context) { g_clear_pointer(&context, g_main_context_unref); }); |
4107 | + |
4108 | + appStartedObservers.emplace(std::make_pair( |
4109 | + std::make_pair(observer, user_data), |
4110 | + core::ScopedConnection( |
4111 | + ubuntu::app_launch::Registry::appStarted().connect([context, observer, user_data](std::shared_ptr<ubuntu::app_launch::Application> app, std::shared_ptr<ubuntu::app_launch::Application::Instance> instance) { |
4112 | + std::string appid = app->appId(); |
4113 | + executeOnContext(context, [appid, observer, user_data]() { |
4114 | + observer(appid.c_str(), user_data); |
4115 | + }); |
4116 | + }) |
4117 | + ) |
4118 | + )); |
4119 | + |
4120 | + return TRUE; |
4121 | +} |
4122 | + |
4123 | +gboolean |
4124 | +ubuntu_app_launch_observer_delete_app_started (UbuntuAppLaunchAppObserver observer, gpointer user_data) |
4125 | +{ |
4126 | + auto iter = appStartedObservers.find(std::make_pair(observer, user_data)); |
4127 | + |
4128 | + if (iter == appStartedObservers.end()) { |
4129 | + return FALSE; |
4130 | + } |
4131 | + |
4132 | + appStartedObservers.erase(iter); |
4133 | + return TRUE; |
4134 | +} |
4135 | + |
4136 | +/* Map of all the observers listening for app stopped */ |
4137 | +static std::map<std::pair<UbuntuAppLaunchAppObserver, gpointer>, core::ScopedConnection> appStoppedObservers; |
4138 | |
4139 | gboolean |
4140 | ubuntu_app_launch_observer_add_app_stop (UbuntuAppLaunchAppObserver observer, gpointer user_data) |
4141 | { |
4142 | - return add_app_generic(observer, user_data, "stopped", &stop_array); |
4143 | + auto context = std::shared_ptr<GMainContext>(g_main_context_ref_thread_default(), [](GMainContext * context) { g_clear_pointer(&context, g_main_context_unref); }); |
4144 | + |
4145 | + appStoppedObservers.emplace(std::make_pair( |
4146 | + std::make_pair(observer, user_data), |
4147 | + core::ScopedConnection( |
4148 | + ubuntu::app_launch::Registry::appStopped().connect([context, observer, user_data](std::shared_ptr<ubuntu::app_launch::Application> app, std::shared_ptr<ubuntu::app_launch::Application::Instance> instance) { |
4149 | + std::string appid = app->appId(); |
4150 | + executeOnContext(context, [appid, observer, user_data]() { |
4151 | + observer(appid.c_str(), user_data); |
4152 | + }); |
4153 | + }) |
4154 | + ) |
4155 | + )); |
4156 | + |
4157 | + return TRUE; |
4158 | } |
4159 | |
4160 | -/* Creates the observer structure and registers for the signal with |
4161 | - GDBus so that we can get a callback */ |
4162 | -static gboolean |
4163 | -add_session_generic (UbuntuAppLaunchAppObserver observer, gpointer user_data, const gchar * signal, GList ** list, GDBusSignalCallback session_cb) |
4164 | +gboolean |
4165 | +ubuntu_app_launch_observer_delete_app_stop (UbuntuAppLaunchAppObserver observer, gpointer user_data) |
4166 | { |
4167 | - GDBusConnection * conn = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL); |
4168 | + auto iter = appStoppedObservers.find(std::make_pair(observer, user_data)); |
4169 | |
4170 | - if (conn == NULL) { |
4171 | + if (iter == appStoppedObservers.end()) { |
4172 | return FALSE; |
4173 | } |
4174 | |
4175 | - observer_t * observert = g_new0(observer_t, 1); |
4176 | - |
4177 | - observert->conn = conn; |
4178 | - observert->func = observer; |
4179 | - observert->user_data = user_data; |
4180 | - |
4181 | - *list = g_list_prepend(*list, observert); |
4182 | - |
4183 | - observert->sighandle = g_dbus_connection_signal_subscribe(conn, |
4184 | - NULL, /* sender */ |
4185 | - "com.canonical.UbuntuAppLaunch", /* interface */ |
4186 | - signal, /* signal */ |
4187 | - "/", /* path */ |
4188 | - NULL, /* arg0 */ |
4189 | - G_DBUS_SIGNAL_FLAGS_NONE, |
4190 | - session_cb, |
4191 | - observert, |
4192 | - NULL); /* user data destroy */ |
4193 | - |
4194 | + appStoppedObservers.erase(iter); |
4195 | return TRUE; |
4196 | } |
4197 | |
4198 | +class CManager : public ubuntu::app_launch::Registry::Manager |
4199 | +{ |
4200 | +public: |
4201 | + CManager () { |
4202 | + g_debug("Creating the CManager object"); |
4203 | + } |
4204 | + |
4205 | + void startingRequest(std::shared_ptr<ubuntu::app_launch::Application> app, |
4206 | + std::shared_ptr<ubuntu::app_launch::Application::Instance> instance, |
4207 | + std::function<void(bool)> reply) override { |
4208 | + std::string sappid = app->appId(); |
4209 | + g_debug("CManager starting: %s", sappid.c_str()); |
4210 | + |
4211 | + for (const auto &data : startingList) { |
4212 | + executeOnContext(data.context, [data, sappid]() { |
4213 | + data.observer(sappid.c_str(), data.user_data); |
4214 | + }); |
4215 | + } |
4216 | + |
4217 | + reply(true); |
4218 | + } |
4219 | + |
4220 | + void focusRequest(std::shared_ptr<ubuntu::app_launch::Application> app, |
4221 | + std::shared_ptr<ubuntu::app_launch::Application::Instance> instance, |
4222 | + std::function<void(bool)> reply) override { |
4223 | + std::string sappid = app->appId(); |
4224 | + g_debug("CManager focus: %s", sappid.c_str()); |
4225 | + |
4226 | + for (const auto &data : focusList) { |
4227 | + executeOnContext(data.context, [data, sappid]() { |
4228 | + data.observer(sappid.c_str(), data.user_data); |
4229 | + }); |
4230 | + } |
4231 | + |
4232 | + reply(true); |
4233 | + } |
4234 | + |
4235 | + void resumeRequest(std::shared_ptr<ubuntu::app_launch::Application> app, |
4236 | + std::shared_ptr<ubuntu::app_launch::Application::Instance> instance, |
4237 | + std::function<void(bool)> reply) override { |
4238 | + std::string sappid = app->appId(); |
4239 | + g_debug("CManager resume: %s", sappid.c_str()); |
4240 | + |
4241 | + for (const auto &data : resumeList) { |
4242 | + executeOnContext(data.context, [data, sappid]() { |
4243 | + data.observer(sappid.c_str(), data.user_data); |
4244 | + }); |
4245 | + } |
4246 | + |
4247 | + reply(true); |
4248 | + } |
4249 | + |
4250 | +private: |
4251 | + struct ObserverData { |
4252 | + UbuntuAppLaunchAppObserver observer; |
4253 | + gpointer user_data; |
4254 | + std::shared_ptr<GMainContext> context; |
4255 | + |
4256 | + ObserverData(UbuntuAppLaunchAppObserver obs, gpointer ud) |
4257 | + : observer(obs) |
4258 | + , user_data(ud) { |
4259 | + context = std::shared_ptr<GMainContext>(g_main_context_ref_thread_default(), [](GMainContext * context) { g_clear_pointer(&context, g_main_context_unref); }); |
4260 | + } |
4261 | + }; |
4262 | + |
4263 | + std::list<ObserverData> focusList; |
4264 | + std::list<ObserverData> resumeList; |
4265 | + std::list<ObserverData> startingList; |
4266 | + |
4267 | + bool removeList (std::list<ObserverData> &list, UbuntuAppLaunchAppObserver observer, gpointer user_data) { |
4268 | + auto iter = std::find_if(list.begin(), list.end(), [observer, user_data](const ObserverData &data) { |
4269 | + return data.observer == observer && data.user_data == user_data; |
4270 | + }); |
4271 | + |
4272 | + if (iter == list.end()) { |
4273 | + return false; |
4274 | + } |
4275 | + |
4276 | + list.erase(iter); |
4277 | + return true; |
4278 | + } |
4279 | + |
4280 | +public: |
4281 | + void addFocus (UbuntuAppLaunchAppObserver observer, gpointer user_data) { |
4282 | + focusList.emplace_back(ObserverData(observer, user_data)); |
4283 | + } |
4284 | + void addResume (UbuntuAppLaunchAppObserver observer, gpointer user_data) { |
4285 | + resumeList.emplace_back(ObserverData(observer, user_data)); |
4286 | + } |
4287 | + void addStarting (UbuntuAppLaunchAppObserver observer, gpointer user_data) { |
4288 | + startingList.emplace_back(ObserverData(observer, user_data)); |
4289 | + } |
4290 | + bool deleteFocus (UbuntuAppLaunchAppObserver observer, gpointer user_data) { |
4291 | + return removeList(focusList, observer, user_data); |
4292 | + } |
4293 | + bool deleteResume (UbuntuAppLaunchAppObserver observer, gpointer user_data) { |
4294 | + return removeList(resumeList, observer, user_data); |
4295 | + } |
4296 | + bool deleteStarting (UbuntuAppLaunchAppObserver observer, gpointer user_data) { |
4297 | + return removeList(startingList, observer, user_data); |
4298 | + } |
4299 | +}; |
4300 | + |
4301 | +static std::weak_ptr<CManager> cmanager; |
4302 | + |
4303 | /* Generic handler for a bunch of our signals */ |
4304 | static inline void |
4305 | generic_signal_cb (GDBusConnection * conn, const gchar * sender, const gchar * object, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data) |
4306 | @@ -433,328 +489,158 @@ |
4307 | } |
4308 | } |
4309 | |
4310 | -/* Handle the focus signal when it occurs, call the observer */ |
4311 | -static void |
4312 | -focus_signal_cb (GDBusConnection * conn, const gchar * sender, const gchar * object, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data) |
4313 | -{ |
4314 | - ual_tracepoint(observer_start, "focus"); |
4315 | - |
4316 | - generic_signal_cb(conn, sender, object, interface, signal, params, user_data); |
4317 | - |
4318 | - ual_tracepoint(observer_finish, "focus"); |
4319 | -} |
4320 | - |
4321 | gboolean |
4322 | ubuntu_app_launch_observer_add_app_focus (UbuntuAppLaunchAppObserver observer, gpointer user_data) |
4323 | { |
4324 | - return add_session_generic(observer, user_data, "UnityFocusRequest", &focus_array, focus_signal_cb); |
4325 | + auto manager = cmanager.lock(); |
4326 | + |
4327 | + if (!manager) { |
4328 | + manager = std::make_shared<CManager>(); |
4329 | + ubuntu::app_launch::Registry::setManager(manager, ubuntu::app_launch::Registry::getDefault()); |
4330 | + cmanager = manager; |
4331 | + } |
4332 | + |
4333 | + manager->addFocus(observer, user_data); |
4334 | + return TRUE; |
4335 | } |
4336 | |
4337 | -/* Handle the resume signal when it occurs, call the observer, then send a signal back when we're done */ |
4338 | -static void |
4339 | -resume_signal_cb (GDBusConnection * conn, const gchar * sender, const gchar * object, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data) |
4340 | +gboolean |
4341 | +ubuntu_app_launch_observer_delete_app_focus (UbuntuAppLaunchAppObserver observer, gpointer user_data) |
4342 | { |
4343 | - ual_tracepoint(observer_start, "resume"); |
4344 | - |
4345 | - generic_signal_cb(conn, sender, object, interface, signal, params, user_data); |
4346 | - |
4347 | - GError * error = NULL; |
4348 | - g_dbus_connection_emit_signal(conn, |
4349 | - sender, /* destination */ |
4350 | - "/", /* path */ |
4351 | - "com.canonical.UbuntuAppLaunch", /* interface */ |
4352 | - "UnityResumeResponse", /* signal */ |
4353 | - params, /* params, the same */ |
4354 | - &error); |
4355 | - |
4356 | - if (error != NULL) { |
4357 | - g_warning("Unable to emit response signal: %s", error->message); |
4358 | - g_error_free(error); |
4359 | + auto manager = cmanager.lock(); |
4360 | + |
4361 | + if (!manager) { |
4362 | + return FALSE; |
4363 | } |
4364 | |
4365 | - ual_tracepoint(observer_finish, "resume"); |
4366 | + return manager->deleteFocus(observer, user_data) ? TRUE : FALSE; |
4367 | } |
4368 | |
4369 | gboolean |
4370 | ubuntu_app_launch_observer_add_app_resume (UbuntuAppLaunchAppObserver observer, gpointer user_data) |
4371 | { |
4372 | - return add_session_generic(observer, user_data, "UnityResumeRequest", &resume_array, resume_signal_cb); |
4373 | + auto manager = cmanager.lock(); |
4374 | + |
4375 | + if (!manager) { |
4376 | + manager = std::make_shared<CManager>(); |
4377 | + ubuntu::app_launch::Registry::setManager(manager, ubuntu::app_launch::Registry::getDefault()); |
4378 | + cmanager = manager; |
4379 | + } |
4380 | + |
4381 | + manager->addResume(observer, user_data); |
4382 | + return TRUE; |
4383 | } |
4384 | |
4385 | -/* Handle the starting signal when it occurs, call the observer, then send a signal back when we're done */ |
4386 | -static void |
4387 | -starting_signal_cb (GDBusConnection * conn, const gchar * sender, const gchar * object, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data) |
4388 | +gboolean |
4389 | +ubuntu_app_launch_observer_delete_app_resume (UbuntuAppLaunchAppObserver observer, gpointer user_data) |
4390 | { |
4391 | - ual_tracepoint(observer_start, "starting"); |
4392 | - |
4393 | - generic_signal_cb(conn, sender, object, interface, signal, params, user_data); |
4394 | - |
4395 | - GError * error = NULL; |
4396 | - g_dbus_connection_emit_signal(conn, |
4397 | - sender, /* destination */ |
4398 | - "/", /* path */ |
4399 | - "com.canonical.UbuntuAppLaunch", /* interface */ |
4400 | - "UnityStartingSignal", /* signal */ |
4401 | - params, /* params, the same */ |
4402 | - &error); |
4403 | - |
4404 | - if (error != NULL) { |
4405 | - g_warning("Unable to emit response signal: %s", error->message); |
4406 | - g_error_free(error); |
4407 | + auto manager = cmanager.lock(); |
4408 | + |
4409 | + if (!manager) { |
4410 | + return FALSE; |
4411 | } |
4412 | |
4413 | - ual_tracepoint(observer_finish, "starting"); |
4414 | + return manager->deleteResume(observer, user_data) ? TRUE : FALSE; |
4415 | } |
4416 | |
4417 | gboolean |
4418 | ubuntu_app_launch_observer_add_app_starting (UbuntuAppLaunchAppObserver observer, gpointer user_data) |
4419 | { |
4420 | + auto manager = cmanager.lock(); |
4421 | + |
4422 | + if (!manager) { |
4423 | + manager = std::make_shared<CManager>(); |
4424 | + ubuntu::app_launch::Registry::setManager(manager, ubuntu::app_launch::Registry::getDefault()); |
4425 | + cmanager = manager; |
4426 | + } |
4427 | + |
4428 | ubuntu::app_launch::Registry::Impl::watchingAppStarting(true); |
4429 | - return add_session_generic(observer, user_data, "UnityStartingBroadcast", &starting_array, starting_signal_cb); |
4430 | -} |
4431 | - |
4432 | -/* Handle the failed signal when it occurs, call the observer */ |
4433 | -static void |
4434 | -failed_signal_cb (GDBusConnection * conn, const gchar * sender, const gchar * object, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data) |
4435 | -{ |
4436 | - failed_observer_t * observer = (failed_observer_t *)user_data; |
4437 | - const gchar * appid = NULL; |
4438 | - const gchar * typestr = NULL; |
4439 | - |
4440 | - ual_tracepoint(observer_start, "failed"); |
4441 | - |
4442 | - if (observer->func != NULL) { |
4443 | - UbuntuAppLaunchAppFailed type = UBUNTU_APP_LAUNCH_APP_FAILED_CRASH; |
4444 | - g_variant_get(params, "(&s&s)", &appid, &typestr); |
4445 | - |
4446 | - if (g_strcmp0("crash", typestr) == 0) { |
4447 | - type = UBUNTU_APP_LAUNCH_APP_FAILED_CRASH; |
4448 | - } else if (g_strcmp0("start-failure", typestr) == 0) { |
4449 | - type = UBUNTU_APP_LAUNCH_APP_FAILED_START_FAILURE; |
4450 | - } else { |
4451 | - g_warning("Application failure type '%s' unknown, reporting as a crash", typestr); |
4452 | - } |
4453 | - |
4454 | - observer->func(appid, type, observer->user_data); |
4455 | - } |
4456 | - |
4457 | - ual_tracepoint(observer_finish, "failed"); |
4458 | -} |
4459 | - |
4460 | -gboolean |
4461 | -ubuntu_app_launch_observer_add_app_failed (UbuntuAppLaunchAppFailedObserver observer, gpointer user_data) |
4462 | -{ |
4463 | - GDBusConnection * conn = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL); |
4464 | - |
4465 | - if (conn == NULL) { |
4466 | - return FALSE; |
4467 | - } |
4468 | - |
4469 | - failed_observer_t * observert = g_new0(failed_observer_t, 1); |
4470 | - |
4471 | - observert->conn = conn; |
4472 | - observert->func = observer; |
4473 | - observert->user_data = user_data; |
4474 | - |
4475 | - failed_array = g_list_prepend(failed_array, observert); |
4476 | - |
4477 | - observert->sighandle = g_dbus_connection_signal_subscribe(conn, |
4478 | - NULL, /* sender */ |
4479 | - "com.canonical.UbuntuAppLaunch", /* interface */ |
4480 | - "ApplicationFailed", /* signal */ |
4481 | - "/", /* path */ |
4482 | - NULL, /* arg0 */ |
4483 | - G_DBUS_SIGNAL_FLAGS_NONE, |
4484 | - failed_signal_cb, |
4485 | - observert, |
4486 | - NULL); /* user data destroy */ |
4487 | - |
4488 | - return TRUE; |
4489 | -} |
4490 | - |
4491 | -/* Handle the paused signal when it occurs, call the observer */ |
4492 | -static void |
4493 | -paused_signal_cb (GDBusConnection * conn, const gchar * sender, const gchar * object, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data) |
4494 | -{ |
4495 | - paused_resumed_observer_t * observer = (paused_resumed_observer_t *)user_data; |
4496 | - |
4497 | - ual_tracepoint(observer_start, observer->lttng_signal); |
4498 | - |
4499 | - if (observer->func != NULL) { |
4500 | - GArray * pidarray = g_array_new(TRUE, TRUE, sizeof(GPid)); |
4501 | - GVariant * appid = g_variant_get_child_value(params, 0); |
4502 | - GVariant * pids = g_variant_get_child_value(params, 1); |
4503 | - guint64 pid; |
4504 | - GVariantIter thispid; |
4505 | - g_variant_iter_init(&thispid, pids); |
4506 | - |
4507 | - while (g_variant_iter_loop(&thispid, "t", &pid)) { |
4508 | - GPid gpid = (GPid)pid; /* Should be a no-op for most architectures, but just in case */ |
4509 | - g_array_append_val(pidarray, gpid); |
4510 | - } |
4511 | - |
4512 | - observer->func(g_variant_get_string(appid, NULL), (GPid *)pidarray->data, observer->user_data); |
4513 | - |
4514 | - g_array_free(pidarray, TRUE); |
4515 | - g_variant_unref(appid); |
4516 | - g_variant_unref(pids); |
4517 | - } |
4518 | - |
4519 | - ual_tracepoint(observer_finish, observer->lttng_signal); |
4520 | -} |
4521 | - |
4522 | -static gboolean |
4523 | -paused_resumed_generic (UbuntuAppLaunchAppPausedResumedObserver observer, gpointer user_data, GList ** queue, const gchar * signal_name, const gchar * lttng_signal) |
4524 | -{ |
4525 | - GDBusConnection * conn = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL); |
4526 | - |
4527 | - if (conn == NULL) { |
4528 | - return FALSE; |
4529 | - } |
4530 | - |
4531 | - paused_resumed_observer_t * observert = g_new0(paused_resumed_observer_t, 1); |
4532 | - |
4533 | - observert->conn = conn; |
4534 | - observert->func = observer; |
4535 | - observert->user_data = user_data; |
4536 | - observert->lttng_signal = lttng_signal; |
4537 | - |
4538 | - *queue = g_list_prepend(*queue, observert); |
4539 | - |
4540 | - observert->sighandle = g_dbus_connection_signal_subscribe(conn, |
4541 | - NULL, /* sender */ |
4542 | - "com.canonical.UbuntuAppLaunch", /* interface */ |
4543 | - signal_name, /* signal */ |
4544 | - "/", /* path */ |
4545 | - NULL, /* arg0 */ |
4546 | - G_DBUS_SIGNAL_FLAGS_NONE, |
4547 | - paused_signal_cb, |
4548 | - observert, |
4549 | - NULL); /* user data destroy */ |
4550 | - |
4551 | - return TRUE; |
4552 | -} |
4553 | - |
4554 | -gboolean |
4555 | -ubuntu_app_launch_observer_add_app_paused (UbuntuAppLaunchAppPausedResumedObserver observer, gpointer user_data) |
4556 | -{ |
4557 | - return paused_resumed_generic(observer, user_data, &paused_array, "ApplicationPaused", "paused"); |
4558 | -} |
4559 | - |
4560 | -gboolean |
4561 | -ubuntu_app_launch_observer_add_app_resumed (UbuntuAppLaunchAppPausedResumedObserver observer, gpointer user_data) |
4562 | -{ |
4563 | - return paused_resumed_generic(observer, user_data, &resumed_array, "ApplicationResumed", "resumed"); |
4564 | -} |
4565 | - |
4566 | -static gboolean |
4567 | -delete_app_generic (UbuntuAppLaunchAppObserver observer, gpointer user_data, GList ** list) |
4568 | -{ |
4569 | - observer_t * observert = NULL; |
4570 | - GList * look; |
4571 | - |
4572 | - for (look = *list; look != NULL; look = g_list_next(look)) { |
4573 | - observert = (observer_t *)look->data; |
4574 | - |
4575 | - if (observert->func == observer && observert->user_data == user_data) { |
4576 | - break; |
4577 | - } |
4578 | - } |
4579 | - |
4580 | - if (look == NULL) { |
4581 | - return FALSE; |
4582 | - } |
4583 | - |
4584 | - g_dbus_connection_signal_unsubscribe(observert->conn, observert->sighandle); |
4585 | - g_object_unref(observert->conn); |
4586 | - |
4587 | - g_free(observert); |
4588 | - *list = g_list_delete_link(*list, look); |
4589 | - |
4590 | - return TRUE; |
4591 | -} |
4592 | - |
4593 | -gboolean |
4594 | -ubuntu_app_launch_observer_delete_app_started (UbuntuAppLaunchAppObserver observer, gpointer user_data) |
4595 | -{ |
4596 | - return delete_app_generic(observer, user_data, &started_array); |
4597 | -} |
4598 | - |
4599 | -gboolean |
4600 | -ubuntu_app_launch_observer_delete_app_stop (UbuntuAppLaunchAppObserver observer, gpointer user_data) |
4601 | -{ |
4602 | - return delete_app_generic(observer, user_data, &stop_array); |
4603 | -} |
4604 | - |
4605 | -gboolean |
4606 | -ubuntu_app_launch_observer_delete_app_resume (UbuntuAppLaunchAppObserver observer, gpointer user_data) |
4607 | -{ |
4608 | - return delete_app_generic(observer, user_data, &resume_array); |
4609 | -} |
4610 | - |
4611 | -gboolean |
4612 | -ubuntu_app_launch_observer_delete_app_focus (UbuntuAppLaunchAppObserver observer, gpointer user_data) |
4613 | -{ |
4614 | - return delete_app_generic(observer, user_data, &focus_array); |
4615 | + manager->addStarting(observer, user_data); |
4616 | + return TRUE; |
4617 | } |
4618 | |
4619 | gboolean |
4620 | ubuntu_app_launch_observer_delete_app_starting (UbuntuAppLaunchAppObserver observer, gpointer user_data) |
4621 | { |
4622 | + auto manager = cmanager.lock(); |
4623 | + |
4624 | + if (!manager) { |
4625 | + return FALSE; |
4626 | + } |
4627 | + |
4628 | ubuntu::app_launch::Registry::Impl::watchingAppStarting(false); |
4629 | - return delete_app_generic(observer, user_data, &starting_array); |
4630 | + return manager->deleteStarting(observer, user_data) ? TRUE : FALSE; |
4631 | +} |
4632 | + |
4633 | +/* Map of all the observers listening for app stopped */ |
4634 | +static std::map<std::pair<UbuntuAppLaunchAppFailedObserver, gpointer>, core::ScopedConnection> appFailedObservers; |
4635 | + |
4636 | +gboolean |
4637 | +ubuntu_app_launch_observer_add_app_failed (UbuntuAppLaunchAppFailedObserver observer, gpointer user_data) |
4638 | +{ |
4639 | + auto context = std::shared_ptr<GMainContext>(g_main_context_ref_thread_default(), [](GMainContext * context) { g_clear_pointer(&context, g_main_context_unref); }); |
4640 | + |
4641 | + appFailedObservers.emplace(std::make_pair( |
4642 | + std::make_pair(observer, user_data), |
4643 | + core::ScopedConnection( |
4644 | + ubuntu::app_launch::Registry::appFailed().connect([context, observer, user_data](std::shared_ptr<ubuntu::app_launch::Application> app, std::shared_ptr<ubuntu::app_launch::Application::Instance> instance, ubuntu::app_launch::Registry::FailureType type) { |
4645 | + std::string appid = app->appId(); |
4646 | + executeOnContext(context, [appid, type, observer, user_data]() { |
4647 | + UbuntuAppLaunchAppFailed ctype; |
4648 | + |
4649 | + switch (type) { |
4650 | + case ubuntu::app_launch::Registry::FailureType::CRASH: |
4651 | + ctype = UBUNTU_APP_LAUNCH_APP_FAILED_CRASH; |
4652 | + break; |
4653 | + case ubuntu::app_launch::Registry::FailureType::START_FAILURE: |
4654 | + ctype = UBUNTU_APP_LAUNCH_APP_FAILED_START_FAILURE; |
4655 | + break; |
4656 | + } |
4657 | + |
4658 | + observer(appid.c_str(), ctype, user_data); |
4659 | + }); |
4660 | + }) |
4661 | + ) |
4662 | + )); |
4663 | + |
4664 | + return TRUE; |
4665 | } |
4666 | |
4667 | gboolean |
4668 | ubuntu_app_launch_observer_delete_app_failed (UbuntuAppLaunchAppFailedObserver observer, gpointer user_data) |
4669 | { |
4670 | - failed_observer_t * observert = NULL; |
4671 | - GList * look; |
4672 | - |
4673 | - for (look = failed_array; look != NULL; look = g_list_next(look)) { |
4674 | - observert = (failed_observer_t *)look->data; |
4675 | - |
4676 | - if (observert->func == observer && observert->user_data == user_data) { |
4677 | - break; |
4678 | - } |
4679 | - } |
4680 | - |
4681 | - if (look == NULL) { |
4682 | + auto iter = appFailedObservers.find(std::make_pair(observer, user_data)); |
4683 | + |
4684 | + if (iter == appFailedObservers.end()) { |
4685 | return FALSE; |
4686 | } |
4687 | |
4688 | - g_dbus_connection_signal_unsubscribe(observert->conn, observert->sighandle); |
4689 | - g_object_unref(observert->conn); |
4690 | - |
4691 | - g_free(observert); |
4692 | - failed_array = g_list_delete_link(failed_array, look); |
4693 | - |
4694 | + appFailedObservers.erase(iter); |
4695 | return TRUE; |
4696 | } |
4697 | |
4698 | -static gboolean |
4699 | -paused_resumed_delete (UbuntuAppLaunchAppPausedResumedObserver observer, gpointer user_data, GList ** list) |
4700 | +static std::map<std::pair<UbuntuAppLaunchAppPausedResumedObserver, gpointer>, core::ScopedConnection> appPausedObservers; |
4701 | + |
4702 | +gboolean |
4703 | +ubuntu_app_launch_observer_add_app_paused (UbuntuAppLaunchAppPausedResumedObserver observer, gpointer user_data) |
4704 | { |
4705 | - paused_resumed_observer_t * observert = NULL; |
4706 | - GList * look; |
4707 | - |
4708 | - for (look = *list; look != NULL; look = g_list_next(look)) { |
4709 | - observert = (paused_resumed_observer_t *)look->data; |
4710 | - |
4711 | - if (observert->func == observer && observert->user_data == user_data) { |
4712 | - break; |
4713 | - } |
4714 | - } |
4715 | - |
4716 | - if (look == NULL) { |
4717 | - return FALSE; |
4718 | - } |
4719 | - |
4720 | - g_dbus_connection_signal_unsubscribe(observert->conn, observert->sighandle); |
4721 | - g_object_unref(observert->conn); |
4722 | - |
4723 | - g_free(observert); |
4724 | - *list = g_list_delete_link(*list, look); |
4725 | + auto context = std::shared_ptr<GMainContext>(g_main_context_ref_thread_default(), [](GMainContext * context) { g_clear_pointer(&context, g_main_context_unref); }); |
4726 | + |
4727 | + appPausedObservers.emplace(std::make_pair( |
4728 | + std::make_pair(observer, user_data), |
4729 | + core::ScopedConnection( |
4730 | + ubuntu::app_launch::Registry::appPaused().connect([context, observer, user_data](std::shared_ptr<ubuntu::app_launch::Application> app, std::shared_ptr<ubuntu::app_launch::Application::Instance> instance, std::vector<pid_t> &pids) { |
4731 | + std::vector<pid_t> lpids = pids; |
4732 | + lpids.emplace_back(0); |
4733 | + |
4734 | + std::string appid = app->appId(); |
4735 | + |
4736 | + executeOnContext(context, [appid, observer, user_data, lpids]() { |
4737 | + observer(appid.c_str(), (int *)(lpids.data()), user_data); |
4738 | + }); |
4739 | + }) |
4740 | + ) |
4741 | + )); |
4742 | |
4743 | return TRUE; |
4744 | } |
4745 | @@ -762,13 +648,53 @@ |
4746 | gboolean |
4747 | ubuntu_app_launch_observer_delete_app_paused (UbuntuAppLaunchAppPausedResumedObserver observer, gpointer user_data) |
4748 | { |
4749 | - return paused_resumed_delete(observer, user_data, &paused_array); |
4750 | + auto iter = appPausedObservers.find(std::make_pair(observer, user_data)); |
4751 | + |
4752 | + if (iter == appPausedObservers.end()) { |
4753 | + return FALSE; |
4754 | + } |
4755 | + |
4756 | + appPausedObservers.erase(iter); |
4757 | + return TRUE; |
4758 | +} |
4759 | + |
4760 | +static std::map<std::pair<UbuntuAppLaunchAppPausedResumedObserver, gpointer>, core::ScopedConnection> appResumedObservers; |
4761 | + |
4762 | +gboolean |
4763 | +ubuntu_app_launch_observer_add_app_resumed (UbuntuAppLaunchAppPausedResumedObserver observer, gpointer user_data) |
4764 | +{ |
4765 | + auto context = std::shared_ptr<GMainContext>(g_main_context_ref_thread_default(), [](GMainContext * context) { g_clear_pointer(&context, g_main_context_unref); }); |
4766 | + |
4767 | + appResumedObservers.emplace(std::make_pair( |
4768 | + std::make_pair(observer, user_data), |
4769 | + core::ScopedConnection( |
4770 | + ubuntu::app_launch::Registry::appResumed().connect([context, observer, user_data](std::shared_ptr<ubuntu::app_launch::Application> app, std::shared_ptr<ubuntu::app_launch::Application::Instance> instance, std::vector<pid_t>& pids) { |
4771 | + std::vector<pid_t> lpids = pids; |
4772 | + lpids.emplace_back(0); |
4773 | + |
4774 | + std::string appid = app->appId(); |
4775 | + |
4776 | + executeOnContext(context, [appid, observer, user_data, lpids]() { |
4777 | + observer(appid.c_str(), (int *)(lpids.data()), user_data); |
4778 | + }); |
4779 | + }) |
4780 | + ) |
4781 | + )); |
4782 | + |
4783 | + return TRUE; |
4784 | } |
4785 | |
4786 | gboolean |
4787 | ubuntu_app_launch_observer_delete_app_resumed (UbuntuAppLaunchAppPausedResumedObserver observer, gpointer user_data) |
4788 | { |
4789 | - return paused_resumed_delete(observer, user_data, &resumed_array); |
4790 | + auto iter = appResumedObservers.find(std::make_pair(observer, user_data)); |
4791 | + |
4792 | + if (iter == appResumedObservers.end()) { |
4793 | + return FALSE; |
4794 | + } |
4795 | + |
4796 | + appResumedObservers.erase(iter); |
4797 | + return TRUE; |
4798 | } |
4799 | |
4800 | typedef void (*per_instance_func_t) (GDBusConnection * con, GVariant * prop_dict, gpointer user_data); |
4801 | |
4802 | === modified file 'tests/CMakeLists.txt' |
4803 | --- tests/CMakeLists.txt 2016-09-14 16:43:36 +0000 |
4804 | +++ tests/CMakeLists.txt 2016-11-10 21:58:30 +0000 |
4805 | @@ -157,6 +157,7 @@ |
4806 | |
4807 | add_custom_target(format-tests |
4808 | COMMAND clang-format -i -style=file |
4809 | + failure-test.cc |
4810 | application-info-desktop.cpp |
4811 | libual-cpp-test.cc |
4812 | list-apps.cpp |
4813 | |
4814 | === modified file 'tests/failure-test.cc' |
4815 | --- tests/failure-test.cc 2016-08-25 18:13:44 +0000 |
4816 | +++ tests/failure-test.cc 2016-11-10 21:58:30 +0000 |
4817 | @@ -17,120 +17,147 @@ |
4818 | * Ted Gould <ted.gould@canonical.com> |
4819 | */ |
4820 | |
4821 | +#include "eventually-fixture.h" |
4822 | +#include "registry.h" |
4823 | +#include <gio/gio.h> |
4824 | +#include <glib/gstdio.h> |
4825 | #include <gtest/gtest.h> |
4826 | -#include <glib/gstdio.h> |
4827 | -#include <gio/gio.h> |
4828 | -#include <ubuntu-app-launch.h> |
4829 | -#include "eventually-fixture.h" |
4830 | |
4831 | class FailureTest : public EventuallyFixture |
4832 | { |
4833 | - private: |
4834 | - GTestDBus * testbus = NULL; |
4835 | - |
4836 | - protected: |
4837 | - virtual void SetUp() { |
4838 | - testbus = g_test_dbus_new(G_TEST_DBUS_NONE); |
4839 | - g_test_dbus_up(testbus); |
4840 | - } |
4841 | - |
4842 | - virtual void TearDown() { |
4843 | - g_test_dbus_down(testbus); |
4844 | - g_clear_object(&testbus); |
4845 | - } |
4846 | +private: |
4847 | + GTestDBus* testbus = NULL; |
4848 | + |
4849 | +protected: |
4850 | + std::shared_ptr<ubuntu::app_launch::Registry> registry; |
4851 | + |
4852 | + virtual void SetUp() |
4853 | + { |
4854 | + /* Click DB test mode */ |
4855 | + g_setenv("TEST_CLICK_DB", "click-db-dir", TRUE); |
4856 | + g_setenv("TEST_CLICK_USER", "test-user", TRUE); |
4857 | + |
4858 | + gchar* linkfarmpath = g_build_filename(CMAKE_SOURCE_DIR, "link-farm", NULL); |
4859 | + g_setenv("UBUNTU_APP_LAUNCH_LINK_FARM", linkfarmpath, TRUE); |
4860 | + g_free(linkfarmpath); |
4861 | + |
4862 | + g_setenv("XDG_DATA_DIRS", CMAKE_SOURCE_DIR, TRUE); |
4863 | + g_setenv("XDG_CACHE_HOME", CMAKE_SOURCE_DIR "/libertine-data", TRUE); |
4864 | + g_setenv("XDG_DATA_HOME", CMAKE_SOURCE_DIR "/libertine-home", TRUE); |
4865 | + |
4866 | + testbus = g_test_dbus_new(G_TEST_DBUS_NONE); |
4867 | + g_test_dbus_up(testbus); |
4868 | + |
4869 | + registry = std::make_shared<ubuntu::app_launch::Registry>(); |
4870 | + } |
4871 | + |
4872 | + virtual void TearDown() |
4873 | + { |
4874 | + registry.reset(); |
4875 | + |
4876 | + g_test_dbus_down(testbus); |
4877 | + g_clear_object(&testbus); |
4878 | + } |
4879 | }; |
4880 | |
4881 | -static void |
4882 | -failed_observer (const gchar * appid, UbuntuAppLaunchAppFailed reason, gpointer user_data) |
4883 | -{ |
4884 | - if (reason == UBUNTU_APP_LAUNCH_APP_FAILED_CRASH) { |
4885 | - std::string * last = static_cast<std::string *>(user_data); |
4886 | - *last = appid; |
4887 | - } |
4888 | -} |
4889 | - |
4890 | TEST_F(FailureTest, CrashTest) |
4891 | { |
4892 | - g_setenv("EXIT_STATUS", "-100", TRUE); |
4893 | - g_setenv("JOB", "application-click", TRUE); |
4894 | - g_setenv("INSTANCE", "foo", TRUE); |
4895 | - |
4896 | - std::string last_observer; |
4897 | - ASSERT_TRUE(ubuntu_app_launch_observer_add_app_failed(failed_observer, &last_observer)); |
4898 | - |
4899 | - /* Status based */ |
4900 | - ASSERT_TRUE(g_spawn_command_line_sync(APP_FAILED_TOOL, NULL, NULL, NULL, NULL)); |
4901 | - |
4902 | - EXPECT_EVENTUALLY_EQ("foo", last_observer); |
4903 | - |
4904 | - last_observer.clear(); |
4905 | - g_unsetenv("EXIT_STATUS"); |
4906 | - g_setenv("EXIT_SIGNAL", "KILL", TRUE); |
4907 | - |
4908 | - /* Signal based */ |
4909 | - ASSERT_TRUE(g_spawn_command_line_sync(APP_FAILED_TOOL, NULL, NULL, NULL, NULL)); |
4910 | - |
4911 | - EXPECT_EVENTUALLY_EQ("foo", last_observer); |
4912 | - |
4913 | - ASSERT_TRUE(ubuntu_app_launch_observer_delete_app_failed(failed_observer, &last_observer)); |
4914 | + g_setenv("EXIT_STATUS", "-100", TRUE); |
4915 | + g_setenv("JOB", "application-click", TRUE); |
4916 | + g_setenv("INSTANCE", "foo", TRUE); |
4917 | + |
4918 | + std::string last_observer; |
4919 | + ubuntu::app_launch::Registry::appFailed(registry).connect( |
4920 | + [&last_observer](std::shared_ptr<ubuntu::app_launch::Application> app, |
4921 | + std::shared_ptr<ubuntu::app_launch::Application::Instance> instance, |
4922 | + ubuntu::app_launch::Registry::FailureType type) { |
4923 | + if (type == ubuntu::app_launch::Registry::FailureType::CRASH) |
4924 | + { |
4925 | + last_observer = app->appId(); |
4926 | + } |
4927 | + }); |
4928 | + |
4929 | + /* Status based */ |
4930 | + ASSERT_TRUE(g_spawn_command_line_sync(APP_FAILED_TOOL, NULL, NULL, NULL, NULL)); |
4931 | + |
4932 | + EXPECT_EVENTUALLY_EQ("foo", last_observer); |
4933 | + |
4934 | + last_observer.clear(); |
4935 | + g_unsetenv("EXIT_STATUS"); |
4936 | + g_setenv("EXIT_SIGNAL", "KILL", TRUE); |
4937 | + |
4938 | + /* Signal based */ |
4939 | + ASSERT_TRUE(g_spawn_command_line_sync(APP_FAILED_TOOL, NULL, NULL, NULL, NULL)); |
4940 | + |
4941 | + EXPECT_EVENTUALLY_EQ("foo", last_observer); |
4942 | } |
4943 | |
4944 | TEST_F(FailureTest, LegacyTest) |
4945 | { |
4946 | - g_setenv("EXIT_STATUS", "-100", TRUE); |
4947 | - g_setenv("JOB", "application-legacy", TRUE); |
4948 | - g_setenv("INSTANCE", "foo-1234", TRUE); |
4949 | - |
4950 | - std::string last_observer; |
4951 | - ASSERT_TRUE(ubuntu_app_launch_observer_add_app_failed(failed_observer, &last_observer)); |
4952 | - |
4953 | - /* Status based */ |
4954 | - ASSERT_TRUE(g_spawn_command_line_sync(APP_FAILED_TOOL, NULL, NULL, NULL, NULL)); |
4955 | - |
4956 | - EXPECT_EVENTUALLY_EQ("foo", last_observer); |
4957 | - |
4958 | - ASSERT_TRUE(ubuntu_app_launch_observer_delete_app_failed(failed_observer, &last_observer)); |
4959 | + g_setenv("EXIT_STATUS", "-100", TRUE); |
4960 | + g_setenv("JOB", "application-legacy", TRUE); |
4961 | + g_setenv("INSTANCE", "foo-1234", TRUE); |
4962 | + |
4963 | + std::string last_observer; |
4964 | + ubuntu::app_launch::Registry::appFailed(registry).connect( |
4965 | + [&last_observer](std::shared_ptr<ubuntu::app_launch::Application> app, |
4966 | + std::shared_ptr<ubuntu::app_launch::Application::Instance> instance, |
4967 | + ubuntu::app_launch::Registry::FailureType type) { |
4968 | + g_debug("Signal handler called"); |
4969 | + if (type == ubuntu::app_launch::Registry::FailureType::CRASH) |
4970 | + { |
4971 | + last_observer = app->appId(); |
4972 | + } |
4973 | + }); |
4974 | + |
4975 | + /* Status based */ |
4976 | + ASSERT_TRUE(g_spawn_command_line_sync(APP_FAILED_TOOL, NULL, NULL, NULL, NULL)); |
4977 | + |
4978 | + EXPECT_EVENTUALLY_EQ("foo", last_observer); |
4979 | } |
4980 | |
4981 | TEST_F(FailureTest, SnapTest) |
4982 | { |
4983 | - g_setenv("EXIT_STATUS", "-100", TRUE); |
4984 | - g_setenv("JOB", "application-snap", TRUE); |
4985 | - g_setenv("INSTANCE", "foo_bar_x123-1234", TRUE); |
4986 | - |
4987 | - std::string last_observer; |
4988 | - ASSERT_TRUE(ubuntu_app_launch_observer_add_app_failed(failed_observer, &last_observer)); |
4989 | - |
4990 | - /* Status based */ |
4991 | - ASSERT_TRUE(g_spawn_command_line_sync(APP_FAILED_TOOL, NULL, NULL, NULL, NULL)); |
4992 | - |
4993 | - EXPECT_EVENTUALLY_EQ("foo_bar_x123", last_observer); |
4994 | - |
4995 | - ASSERT_TRUE(ubuntu_app_launch_observer_delete_app_failed(failed_observer, &last_observer)); |
4996 | -} |
4997 | - |
4998 | -static void |
4999 | -failed_start_observer (const gchar * appid, UbuntuAppLaunchAppFailed reason, gpointer user_data) |
5000 | -{ |
The diff has been truncated for viewing.