23
23
#include "libupstart-app-launch/upstart-app-launch.h"
24
24
#include "helpers.h"
25
25
#include "second-exec-core.h"
26
#include "second-exec-trace.h"
30
GMainLoop * mainloop = NULL;
31
guint connections_open = 0;
32
const gchar * appid = NULL;
33
const gchar * input_uris = NULL;
34
GVariant * app_data = NULL;
35
gchar * dbus_path = NULL;
36
guint64 unity_starttime = 0;
26
#include "upstart-app-launch-trace.h"
29
GDBusConnection * bus;
33
guint connections_open;
36
guint64 unity_starttime;
40
static void second_exec_complete (second_exec_t * data);
39
42
/* Unity didn't respond in time, continue on */
41
44
timer_cb (gpointer user_data)
43
tracepoint(upstart_app_launch, second_exec_resume_timeout);
46
tracepoint(upstart_app_launch, second_exec_resume_timeout, ((second_exec_t *)user_data)->appid);
44
47
g_warning("Unity didn't respond in 500ms to resume the app");
45
g_main_loop_quit(mainloop);
48
second_exec_complete(user_data);
46
49
return G_SOURCE_REMOVE;
49
52
/* Lower the connection count and process if it gets to zero */
51
connection_count_dec (void)
54
connection_count_dec (second_exec_t * data)
53
tracepoint(upstart_app_launch, second_exec_connection_complete);
55
if (connections_open == 0) {
56
tracepoint(upstart_app_launch, second_exec_connection_complete, data->appid);
57
data->connections_open--;
58
if (data->connections_open == 0) {
56
59
g_debug("Finished finding connections");
57
60
/* Check time here, either we've already heard from
58
61
Unity and we should send the data to the app (quit) or
59
62
we should wait some more */
60
guint64 timespent = g_get_monotonic_time() - unity_starttime;
63
guint64 timespent = g_get_monotonic_time() - data->unity_starttime;
61
64
if (timespent > 500 /* ms */ * 1000 /* ms to us */) {
62
g_main_loop_quit(mainloop);
65
second_exec_complete(data);
64
67
g_debug("Timer Set");
65
timer = g_timeout_add(500 - (timespent / 1000), timer_cb, NULL);
68
data->timer = g_timeout_add(500 - (timespent / 1000), timer_cb, data);
74
77
unity_resume_cb (GDBusConnection * connection, const gchar * sender, const gchar * path, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data)
79
second_exec_t * data = (second_exec_t *)user_data;
76
80
g_debug("Unity Completed Resume");
77
tracepoint(upstart_app_launch, second_exec_resume_complete);
81
tracepoint(upstart_app_launch, second_exec_resume_complete, data->appid);
80
g_source_remove(timer);
83
if (data->timer != 0) {
84
g_source_remove(data->timer);
83
if (connections_open == 0) {
84
g_main_loop_quit(mainloop);
88
if (data->connections_open == 0) {
89
second_exec_complete(data);
86
91
/* Make it look like we started *forever* ago */
92
data->unity_starttime = 0;
103
108
gchar ** uri_split = NULL;
104
109
GError * error = NULL;
106
g_shell_parse_argv(input_uris, NULL, &uri_split, &error);
111
g_shell_parse_argv(data->input_uris, NULL, &uri_split, &error);
108
113
if (uri_split == NULL || uri_split[0] == NULL || error != NULL) {
109
114
if (error != NULL) {
110
g_warning("Unable to parse URLs '%s': %s", input_uris, error->message);
115
g_warning("Unable to parse URLs '%s': %s", data->input_uris, error->message);
111
116
g_error_free(error);
136
141
g_variant_builder_add_value(&tuple, uris);
137
142
g_variant_builder_add_value(&tuple, platform);
139
app_data = g_variant_builder_end(&tuple);
140
g_variant_ref_sink(app_data);
144
data->app_data = g_variant_builder_end(&tuple);
145
g_variant_ref_sink(data->app_data);
149
154
http://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#dbus
152
app_id_to_dbus_path (void)
157
app_id_to_dbus_path (second_exec_t * data)
154
if (dbus_path != NULL) {
159
if (data->dbus_path != NULL) {
158
dbus_path = nih_dbus_path(NULL, "", appid, NULL);
159
g_debug("DBus Path: %s", dbus_path);
163
GString * str = g_string_sized_new(strlen(data->appid) + 2); /* base case, we just need a / and a null */
164
g_string_append_c(str, '/');
167
for (i = 0; data->appid[i] != '\0'; i++) {
168
if ((data->appid[i] >= 'a' && data->appid[i] <= 'z') ||
169
(data->appid[i] >= 'A' && data->appid[i] <= 'Z') ||
170
(data->appid[i] >= '0' && data->appid[i] <= '9' && i != 0)) {
171
g_string_append_c(str, data->appid[i]);
175
g_string_append_printf(str, "_%2x", data->appid[i]);
178
data->dbus_path = g_string_free(str, FALSE);
179
g_debug("DBus Path: %s", data->dbus_path);
168
188
GError * error = NULL;
170
tracepoint(upstart_app_launch, second_exec_app_contacted);
190
tracepoint(upstart_app_launch, second_exec_app_contacted, ((second_exec_t *)user_data)->appid);
172
192
g_dbus_connection_call_finish(G_DBUS_CONNECTION(object), res, &error);
174
194
if (error != NULL) {
175
tracepoint(upstart_app_launch, second_exec_app_error);
195
tracepoint(upstart_app_launch, second_exec_app_error, ((second_exec_t *)user_data)->appid);
176
196
/* Mostly just to free the error, but printing for debugging */
177
197
g_debug("Unable to send Open: %s", error->message);
178
198
g_error_free(error);
181
connection_count_dec();
201
connection_count_dec(user_data);
185
205
/* Sends the Open message to the connection with the URIs we were given */
187
contact_app (GDBusConnection * bus, const gchar * dbus_name)
207
contact_app (GDBusConnection * bus, const gchar * dbus_name, second_exec_t * data)
189
tracepoint(upstart_app_launch, second_exec_contact_app);
209
tracepoint(upstart_app_launch, second_exec_contact_app, data->appid, dbus_name);
192
app_id_to_dbus_path();
212
app_id_to_dbus_path(data);
194
214
/* Using the FD.o Application interface */
195
215
g_dbus_connection_call(bus,
198
218
"org.freedesktop.Application",
202
222
G_DBUS_CALL_FLAGS_NONE,
207
227
g_debug("Sending Open request to: %s", dbus_name);
234
second_exec_t * data;
212
237
/* Gets the PID for a connection, and if it matches the one we're looking
213
238
for then it tries to send a message to that connection */
215
240
get_pid_cb (GObject * object, GAsyncResult * res, gpointer user_data)
217
gchar * dbus_name = (gchar *)user_data;
242
get_pid_t * data = (get_pid_t *)user_data;
218
243
GError * error = NULL;
219
244
GVariant * vpid = NULL;
221
tracepoint(upstart_app_launch, second_exec_got_pid);
246
tracepoint(upstart_app_launch, second_exec_got_pid, data->data->appid, data->name);
223
248
vpid = g_dbus_connection_call_finish(G_DBUS_CONNECTION(object), res, &error);
225
250
if (error != NULL) {
226
g_warning("Unable to query PID for dbus name '%s': %s", dbus_name, error->message);
251
g_warning("Unable to query PID for dbus name '%s': %s", data->name, error->message);
227
252
g_error_free(error);
230
254
/* Lowering the connection count, this one is terminal, even if in error */
231
connection_count_dec();
255
connection_count_dec(data->data);
236
264
g_variant_get(vpid, "(u)", &pid);
237
265
g_variant_unref(vpid);
239
if (pid == app_pid) {
267
if (pid == data->data->app_pid) {
240
268
/* Trying to send a message to the connection */
241
contact_app(G_DBUS_CONNECTION(object), dbus_name);
269
contact_app(G_DBUS_CONNECTION(object), data->name, data->data);
243
271
/* See if we can quit now */
244
connection_count_dec();
272
connection_count_dec(data->data);
252
281
/* Starts to look for the PID and the connections for that PID */
254
find_appid_pid (GDBusConnection * session)
283
find_appid_pid (GDBusConnection * session, second_exec_t * data)
256
285
GError * error = NULL;
279
tracepoint(upstart_app_launch, second_exec_got_dbus_names);
308
g_debug("Got bus names");
309
tracepoint(upstart_app_launch, second_exec_got_dbus_names, data->appid);
281
311
/* Next figure out what we're looking for (and if there is something to look for) */
282
312
/* NOTE: We're getting the PID *after* the list of connections so
283
313
that some new process can't come in, be the same PID as it's
284
314
connection will not be in teh list we just got. */
285
app_pid = upstart_app_launch_get_primary_pid(appid);
287
g_warning("Unable to find pid for app id '%s'", appid);
315
data->app_pid = upstart_app_launch_get_primary_pid(data->appid);
316
if (data->app_pid == 0) {
317
g_warning("Unable to find pid for app id '%s'", data->appid);
291
tracepoint(upstart_app_launch, second_exec_got_primary_pid);
321
g_debug("Primary PID: %d", data->app_pid);
322
tracepoint(upstart_app_launch, second_exec_got_primary_pid, data->appid);
293
324
/* Get the names */
294
325
GVariant * names = g_variant_get_child_value(listnames, 0);
301
332
if (!g_dbus_is_unique_name(name)) {
336
get_pid_t * pid_data = g_new0(get_pid_t, 1);
337
pid_data->data = data;
338
pid_data->name = g_strdup(name);
305
tracepoint(upstart_app_launch, second_exec_request_pid);
340
tracepoint(upstart_app_launch, second_exec_request_pid, data->appid, pid_data->name);
307
342
/* Get the PIDs */
308
343
g_dbus_connection_call(session,
346
/* Allocate main loop */
347
mainloop = g_main_loop_new(NULL, FALSE);
378
/* Setup our continuation data */
379
second_exec_t * data = g_new0(second_exec_t, 1);
380
data->appid = g_strdup(app_id);
381
data->input_uris = g_strdup(appuris);
349
384
/* Set up listening for the unfrozen signal from Unity */
350
385
g_dbus_connection_signal_subscribe(session,
352
387
"com.canonical.UpstartAppLaunch", /* interface */
353
388
"UnityResumeResponse", /* signal */
356
391
G_DBUS_SIGNAL_FLAGS_NONE,
357
unity_resume_cb, mainloop,
392
unity_resume_cb, data,
358
393
NULL); /* user data destroy */
360
tracepoint(upstart_app_launch, second_exec_emit_resume);
395
g_debug("Sending resume request");
396
tracepoint(upstart_app_launch, second_exec_emit_resume, app_id);
362
398
/* Send unfreeze to to Unity */
363
399
g_dbus_connection_emit_signal(session,
366
402
"com.canonical.UpstartAppLaunch", /* interface */
367
403
"UnityResumeRequest", /* signal */
368
g_variant_new("(s)", appid),
404
g_variant_new("(s)", app_id),
371
407
/* Now we start a race, we try to get to the point of knowing who
372
408
to send things to, and Unity is unfrezing it. When both are
373
409
done we can send something to the app */
374
unity_starttime = g_get_monotonic_time();
410
data->unity_starttime = g_get_monotonic_time();
376
412
if (error != NULL) {
377
413
/* On error let's not wait for Unity */
378
414
g_warning("Unable to signal Unity: %s", error->message);
379
415
g_error_free(error);
417
data->unity_starttime = 0;
384
420
/* If we've got something to give out, start looking for how */
385
if (input_uris != NULL) {
386
find_appid_pid(session);
421
if (data->input_uris != NULL) {
422
find_appid_pid(session, data);
389
425
/* Loop and wait for everything to align */
390
if (connections_open > 0 || unity_starttime > 0) {
391
g_main_loop_run(mainloop);
426
if (data->connections_open == 0 && data->unity_starttime == 0) {
427
second_exec_complete(data);
393
g_debug("Finishing main loop");
395
tracepoint(upstart_app_launch, second_exec_emit_focus);
434
second_exec_complete (second_exec_t * data)
436
GError * error = NULL;
437
tracepoint(upstart_app_launch, second_exec_emit_focus, data->appid);
397
439
/* Now that we're done sending the info to the app, we can ask
398
440
Unity to focus the application. */
399
g_dbus_connection_emit_signal(session,
441
g_dbus_connection_emit_signal(data->bus,
400
442
NULL, /* destination */
402
444
"com.canonical.UpstartAppLaunch", /* interface */
403
445
"UnityFocusRequest", /* signal */
404
g_variant_new("(s)", appid),
446
g_variant_new("(s)", data->appid),
407
449
if (error != NULL) {
413
455
/* Make sure the signal hits the bus */
414
g_dbus_connection_flush_sync(session, NULL, NULL);
456
g_dbus_connection_flush_sync(data->bus, NULL, &error);
458
g_warning("Unable to flush session bus: %s", error->message);
463
tracepoint(upstart_app_launch, second_exec_finish, data->appid);
417
if (app_data != NULL) {
418
g_variant_unref(app_data);
422
g_main_loop_unref(mainloop);
423
g_object_unref(session);
425
if (dbus_path != NULL) {
430
tracepoint(upstart_app_launch, second_exec_finish);
466
g_object_unref(data->bus);
467
if (data->app_data != NULL)
468
g_variant_unref(data->app_data);
470
g_free(data->input_uris);
471
g_free(data->dbus_path);