~ted/ubuntu-app-launch/snap-icon-unbreak

« back to all changes in this revision

Viewing changes to libupstart-app-launch/second-exec-core.c

Updated to failure-is-an-option

Show diffs side-by-side

added added

removed removed

Lines of Context:
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"
27
 
 
28
 
/* Globals */
29
 
GPid app_pid = 0;
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;
37
 
guint timer = 0;
 
26
#include "upstart-app-launch-trace.h"
 
27
 
 
28
typedef struct {
 
29
        GDBusConnection * bus;
 
30
        gchar * appid;
 
31
        gchar * input_uris;
 
32
        GPid app_pid;
 
33
        guint connections_open;
 
34
        GVariant * app_data;
 
35
        gchar * dbus_path;
 
36
        guint64 unity_starttime;
 
37
        guint timer;
 
38
} second_exec_t;
 
39
 
 
40
static void second_exec_complete (second_exec_t * data);
38
41
 
39
42
/* Unity didn't respond in time, continue on */
40
43
static gboolean
41
44
timer_cb (gpointer user_data)
42
45
{
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;
47
50
}
48
51
 
49
52
/* Lower the connection count and process if it gets to zero */
50
53
static void
51
 
connection_count_dec (void)
 
54
connection_count_dec (second_exec_t * data)
52
55
{
53
 
        tracepoint(upstart_app_launch, second_exec_connection_complete);
54
 
        connections_open--;
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);
63
66
                } else {
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);
66
69
                }
67
70
        }
68
71
        return;
73
76
static void
74
77
unity_resume_cb (GDBusConnection * connection, const gchar * sender, const gchar * path, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data)
75
78
{
 
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);
78
82
 
79
 
        if (timer != 0) {
80
 
                g_source_remove(timer);
 
83
        if (data->timer != 0) {
 
84
                g_source_remove(data->timer);
 
85
                data->timer = 0;
81
86
        }
82
87
 
83
 
        if (connections_open == 0) {
84
 
                g_main_loop_quit(mainloop);
 
88
        if (data->connections_open == 0) {
 
89
                second_exec_complete(data);
85
90
        } else {
86
91
                /* Make it look like we started *forever* ago */
87
 
                unity_starttime = 0;
 
92
                data->unity_starttime = 0;
88
93
        }
89
94
 
90
95
        return;
92
97
 
93
98
/* Turn the input string into something we can send to apps */
94
99
static void
95
 
parse_uris (void)
 
100
parse_uris (second_exec_t * data)
96
101
{
97
 
        if (app_data != NULL) {
 
102
        if (data->app_data != NULL) {
98
103
                /* Already done */
99
104
                return;
100
105
        }
103
108
        gchar ** uri_split = NULL;
104
109
        GError * error = NULL;
105
110
 
106
 
        g_shell_parse_argv(input_uris, NULL, &uri_split, &error);
 
111
        g_shell_parse_argv(data->input_uris, NULL, &uri_split, &error);
107
112
 
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);
112
117
                }
113
118
 
136
141
        g_variant_builder_add_value(&tuple, uris);
137
142
        g_variant_builder_add_value(&tuple, platform);
138
143
 
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);
141
146
 
142
147
        return;
143
148
}
149
154
   http://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#dbus   
150
155
*/
151
156
static void
152
 
app_id_to_dbus_path (void)
 
157
app_id_to_dbus_path (second_exec_t * data)
153
158
{
154
 
        if (dbus_path != NULL) {
 
159
        if (data->dbus_path != NULL) {
155
160
                return;
156
161
        }
157
162
 
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, '/');
 
165
 
 
166
        int i;
 
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]);
 
172
                        continue;
 
173
                }
 
174
 
 
175
                g_string_append_printf(str, "_%2x", data->appid[i]);
 
176
        }
 
177
 
 
178
        data->dbus_path = g_string_free(str, FALSE);
 
179
        g_debug("DBus Path: %s", data->dbus_path);
160
180
 
161
181
        return;
162
182
}
167
187
{
168
188
        GError * error = NULL;
169
189
 
170
 
        tracepoint(upstart_app_launch, second_exec_app_contacted);
 
190
        tracepoint(upstart_app_launch, second_exec_app_contacted, ((second_exec_t *)user_data)->appid);
171
191
 
172
192
        g_dbus_connection_call_finish(G_DBUS_CONNECTION(object), res, &error);
173
193
 
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);
179
199
        }
180
200
 
181
 
        connection_count_dec();
 
201
        connection_count_dec(user_data);
182
202
        return;
183
203
}
184
204
 
185
205
/* Sends the Open message to the connection with the URIs we were given */
186
206
static void
187
 
contact_app (GDBusConnection * bus, const gchar * dbus_name)
 
207
contact_app (GDBusConnection * bus, const gchar * dbus_name, second_exec_t * data)
188
208
{
189
 
        tracepoint(upstart_app_launch, second_exec_contact_app);
 
209
        tracepoint(upstart_app_launch, second_exec_contact_app, data->appid, dbus_name);
190
210
 
191
 
        parse_uris();
192
 
        app_id_to_dbus_path();
 
211
        parse_uris(data);
 
212
        app_id_to_dbus_path(data);
193
213
 
194
214
        /* Using the FD.o Application interface */
195
215
        g_dbus_connection_call(bus,
196
216
                dbus_name,
197
 
                dbus_path,
 
217
                data->dbus_path,
198
218
                "org.freedesktop.Application",
199
219
                "Open",
200
 
                app_data,
 
220
                data->app_data,
201
221
                NULL,
202
222
                G_DBUS_CALL_FLAGS_NONE,
203
223
                -1,
204
224
                NULL,
205
 
                send_open_cb, NULL);
 
225
                send_open_cb, data);
206
226
 
207
227
        g_debug("Sending Open request to: %s", dbus_name);
208
228
 
209
229
        return;
210
230
}
211
231
 
 
232
typedef struct {
 
233
        gchar * name;
 
234
        second_exec_t * data;
 
235
} get_pid_t;
 
236
 
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 */
214
239
static void
215
240
get_pid_cb (GObject * object, GAsyncResult * res, gpointer user_data)
216
241
{
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;
220
245
 
221
 
        tracepoint(upstart_app_launch, second_exec_got_pid);
 
246
        tracepoint(upstart_app_launch, second_exec_got_pid, data->data->appid, data->name);
222
247
 
223
248
        vpid = g_dbus_connection_call_finish(G_DBUS_CONNECTION(object), res, &error);
224
249
 
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);
228
 
                g_free(dbus_name);
229
253
 
230
254
                /* Lowering the connection count, this one is terminal, even if in error */
231
 
                connection_count_dec();
 
255
                connection_count_dec(data->data);
 
256
 
 
257
                g_free(data->name);
 
258
                g_free(data);
 
259
 
232
260
                return;
233
261
        }
234
262
 
236
264
        g_variant_get(vpid, "(u)", &pid);
237
265
        g_variant_unref(vpid);
238
266
 
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);
242
270
        } else {
243
271
                /* See if we can quit now */
244
 
                connection_count_dec();
 
272
                connection_count_dec(data->data);
245
273
        }
246
274
 
247
 
        g_free(dbus_name);
 
275
        g_free(data->name);
 
276
        g_free(data);
248
277
 
249
278
        return;
250
279
}
251
280
 
252
281
/* Starts to look for the PID and the connections for that PID */
253
282
void
254
 
find_appid_pid (GDBusConnection * session)
 
283
find_appid_pid (GDBusConnection * session, second_exec_t * data)
255
284
{
256
285
        GError * error = NULL;
257
286
 
276
305
                return;
277
306
        }
278
307
 
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);
280
310
 
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);
286
 
        if (app_pid == 0) {
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);
288
318
                return;
289
319
        }
290
320
 
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);
292
323
 
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)) {
302
333
                        continue;
303
334
                }
 
335
                
 
336
                get_pid_t * pid_data = g_new0(get_pid_t, 1);
 
337
                pid_data->data = data;
 
338
                pid_data->name = g_strdup(name);
304
339
 
305
 
                tracepoint(upstart_app_launch, second_exec_request_pid);
 
340
                tracepoint(upstart_app_launch, second_exec_request_pid, data->appid, pid_data->name);
306
341
 
307
342
                /* Get the PIDs */
308
343
                g_dbus_connection_call(session,
315
350
                        G_DBUS_CALL_FLAGS_NONE,
316
351
                        -1,
317
352
                        NULL,
318
 
                        get_pid_cb, g_strdup(name));
 
353
                        get_pid_cb, pid_data);
319
354
 
320
 
                connections_open++;
 
355
                data->connections_open++;
321
356
        }
322
357
 
323
358
        g_variant_unref(names);
329
364
gboolean
330
365
second_exec (const gchar * app_id, const gchar * appuris)
331
366
{
332
 
        appid = app_id;
333
 
        input_uris = appuris;
334
 
 
335
 
        tracepoint(upstart_app_launch, second_exec_start);
 
367
        tracepoint(upstart_app_launch, second_exec_start, app_id, appuris);
336
368
 
337
369
        /* DBus tell us! */
338
370
        GError * error = NULL;
343
375
                return FALSE;
344
376
        }
345
377
 
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);
 
382
        data->bus = session;
348
383
 
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 */
354
389
                "/", /* path */
355
 
                appid, /* arg0 */
 
390
                app_id, /* arg0 */
356
391
                G_DBUS_SIGNAL_FLAGS_NONE,
357
 
                unity_resume_cb, mainloop,
 
392
                unity_resume_cb, data,
358
393
                NULL); /* user data destroy */
359
394
 
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);
361
397
 
362
398
        /* Send unfreeze to to Unity */
363
399
        g_dbus_connection_emit_signal(session,
365
401
                "/", /* path */
366
402
                "com.canonical.UpstartAppLaunch", /* interface */
367
403
                "UnityResumeRequest", /* signal */
368
 
                g_variant_new("(s)", appid),
 
404
                g_variant_new("(s)", app_id),
369
405
                &error);
370
406
 
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();
375
411
 
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);
380
416
                error = NULL;
381
 
                unity_starttime = 0;
 
417
                data->unity_starttime = 0;
382
418
        }
383
419
 
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);
387
423
        }
388
424
 
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);
392
428
        }
393
 
        g_debug("Finishing main loop");
394
 
 
395
 
        tracepoint(upstart_app_launch, second_exec_emit_focus);
 
429
 
 
430
        return TRUE;
 
431
}
 
432
 
 
433
static void
 
434
second_exec_complete (second_exec_t * data)
 
435
{
 
436
        GError * error = NULL;
 
437
        tracepoint(upstart_app_launch, second_exec_emit_focus, data->appid);
396
438
 
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 */
401
443
                "/", /* path */
402
444
                "com.canonical.UpstartAppLaunch", /* interface */
403
445
                "UnityFocusRequest", /* signal */
404
 
                g_variant_new("(s)", appid),
 
446
                g_variant_new("(s)", data->appid),
405
447
                &error);
406
448
 
407
449
        if (error != NULL) {
411
453
        }
412
454
 
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);
 
457
        if (error != NULL) {
 
458
                g_warning("Unable to flush session bus: %s", error->message);
 
459
                g_error_free(error);
 
460
                error = NULL;
 
461
        }
 
462
 
 
463
        tracepoint(upstart_app_launch, second_exec_finish, data->appid);
415
464
 
416
465
        /* Clean up */
417
 
        if (app_data != NULL) {
418
 
                g_variant_unref(app_data);
419
 
                app_data = NULL;
420
 
        }
421
 
 
422
 
        g_main_loop_unref(mainloop);
423
 
        g_object_unref(session);
424
 
 
425
 
        if (dbus_path != NULL) {
426
 
                nih_free(dbus_path);
427
 
                dbus_path = NULL;
428
 
        }
429
 
 
430
 
        tracepoint(upstart_app_launch, second_exec_finish);
431
 
 
432
 
        return TRUE;
 
466
        g_object_unref(data->bus);
 
467
        if (data->app_data != NULL)
 
468
                g_variant_unref(data->app_data);
 
469
        g_free(data->appid);
 
470
        g_free(data->input_uris);
 
471
        g_free(data->dbus_path);
 
472
        g_free(data);
 
473
 
 
474
        return;
433
475
}