~ted/ubuntu-app-launch/uri-splitting

62.2.1 by Ted Gould
Add an executable target for fdo-application-open
1
/*
2
 * Copyright 2013 Canonical Ltd.
3
 *
4
 * This program is free software: you can redistribute it and/or modify it
5
 * under the terms of the GNU General Public License version 3, as published
6
 * by the Free Software Foundation.
7
 *
8
 * This program is distributed in the hope that it will be useful, but
9
 * WITHOUT ANY WARRANTY; without even the implied warranties of
10
 * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11
 * PURPOSE.  See the GNU General Public License for more details.
12
 *
13
 * You should have received a copy of the GNU General Public License along
14
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
15
 *
16
 * Authors:
17
 *     Ted Gould <ted.gould@canonical.com>
18
 */
19
62.2.4 by Ted Gould
Flesh this out a bunch so we start getting connections
20
#include <gio/gio.h>
62.2.18 by Ted Gould
Use nih_dbus_path() to get the app path
21
#include <nih/alloc.h>
22
#include <libnih-dbus.h>
62.2.4 by Ted Gould
Flesh this out a bunch so we start getting connections
23
#include "libupstart-app-launch/upstart-app-launch.h"
62.2.8 by Ted Gould
Get the dbus path to use for sending the message from the application name
24
#include "helpers.h"
61.1.31 by Ted Gould
Breaking out the core of second exec to ensure we can play with it.
25
#include "second-exec-core.h"
62.2.4 by Ted Gould
Flesh this out a bunch so we start getting connections
26
27
/* Globals */
28
GPid app_pid = 0;
29
GMainLoop * mainloop = NULL;
30
guint connections_open = 0;
62.2.8 by Ted Gould
Get the dbus path to use for sending the message from the application name
31
const gchar * appid = NULL;
62.2.7 by Ted Gould
Get our URIs into something we can use
32
const gchar * input_uris = NULL;
62.2.10 by Ted Gould
Add in the platform data to what we give to apps
33
GVariant * app_data = NULL;
62.2.8 by Ted Gould
Get the dbus path to use for sending the message from the application name
34
gchar * dbus_path = NULL;
61.1.4 by Ted Gould
Mostly setting up a big set of comments and TODOs to describe what we're gonna do
35
guint64 unity_starttime = 0;
61.1.5 by Ted Gould
Setting up the callback signal from Unity to know that we've resumed
36
guint timer = 0;
37
38
/* Unity didn't respond in time, continue on */
39
static gboolean
40
timer_cb (gpointer user_data)
41
{
42
	g_warning("Unity didn't respond in 500ms to resume the app");
43
	g_main_loop_quit(mainloop);
44
	return G_SOURCE_REMOVE;
45
}
62.2.4 by Ted Gould
Flesh this out a bunch so we start getting connections
46
47
/* Lower the connection count and process if it gets to zero */
48
static void
49
connection_count_dec (void)
50
{
51
	connections_open--;
52
	if (connections_open == 0) {
61.1.29 by Ted Gould
Useful debugging
53
		g_debug("Finished finding connections");
61.1.5 by Ted Gould
Setting up the callback signal from Unity to know that we've resumed
54
		/* Check time here, either we've already heard from
61.1.4 by Ted Gould
Mostly setting up a big set of comments and TODOs to describe what we're gonna do
55
		   Unity and we should send the data to the app (quit) or
56
		   we should wait some more */
61.1.5 by Ted Gould
Setting up the callback signal from Unity to know that we've resumed
57
		guint64 timespent = g_get_monotonic_time() - unity_starttime;
58
		if (timespent > 500 /* ms */ * 1000 /* ms to us */) {
59
			g_main_loop_quit(mainloop);
60
		} else {
61.1.29 by Ted Gould
Useful debugging
61
			g_debug("Timer Set");
61.1.5 by Ted Gould
Setting up the callback signal from Unity to know that we've resumed
62
			timer = g_timeout_add(500 - (timespent / 1000), timer_cb, NULL);
63
		}
64
	}
65
	return;
66
}
67
68
/* Called when Unity is done unfreezing the application, if we're
69
   done determining the PID, we can send signals */
70
static void
71
unity_resume_cb (GDBusConnection * connection, const gchar * sender, const gchar * path, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data)
72
{
61.1.16 by Ted Gould
Make sure the last signal hits the bus
73
	g_debug("Unity Completed Resume");
74
61.1.5 by Ted Gould
Setting up the callback signal from Unity to know that we've resumed
75
	if (timer != 0) {
76
		g_source_remove(timer);
77
	}
78
79
	if (connections_open == 0) {
62.2.4 by Ted Gould
Flesh this out a bunch so we start getting connections
80
		g_main_loop_quit(mainloop);
61.1.5 by Ted Gould
Setting up the callback signal from Unity to know that we've resumed
81
	} else {
82
		/* Make it look like we started *forever* ago */
83
		unity_starttime = 0;
62.2.4 by Ted Gould
Flesh this out a bunch so we start getting connections
84
	}
61.1.5 by Ted Gould
Setting up the callback signal from Unity to know that we've resumed
85
62.2.4 by Ted Gould
Flesh this out a bunch so we start getting connections
86
	return;
87
}
88
62.2.7 by Ted Gould
Get our URIs into something we can use
89
/* Turn the input string into something we can send to apps */
90
static void
91
parse_uris (void)
92
{
62.2.10 by Ted Gould
Add in the platform data to what we give to apps
93
	if (app_data != NULL) {
62.2.7 by Ted Gould
Get our URIs into something we can use
94
		/* Already done */
95
		return;
96
	}
97
62.2.15 by Ted Gould
Adding comment on spaces so we know where to fix
98
	/* TODO: Joining only with space could cause issues with breaking them
99
	   back out.  We don't have any cases of more than one today.  But, this
100
	   isn't good.
101
	   https://bugs.launchpad.net/upstart-app-launch/+bug/1229354
102
	   */
62.2.10 by Ted Gould
Add in the platform data to what we give to apps
103
	GVariant * uris = NULL;
62.2.7 by Ted Gould
Get our URIs into something we can use
104
	gchar ** uri_split = g_strsplit(input_uris, " ", 0);
105
	if (uri_split[0] == NULL) {
106
		g_free(uri_split);
62.2.10 by Ted Gould
Add in the platform data to what we give to apps
107
		uris = g_variant_new_array(G_VARIANT_TYPE_STRING, NULL, 0);
108
	} else {
109
		GVariantBuilder builder;
110
		g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY);
111
112
		int i;
113
		for (i = 0; uri_split[i] != NULL; i++) {
114
			g_variant_builder_add_value(&builder, g_variant_new_take_string(uri_split[i]));
115
		}
116
		g_free(uri_split);
117
118
		uris = g_variant_builder_end(&builder);
119
	}
120
121
	GVariant * platform = g_variant_new_array(G_VARIANT_TYPE("{sv}"), NULL, 0);
122
123
	GVariantBuilder tuple;
124
	g_variant_builder_init(&tuple, G_VARIANT_TYPE_TUPLE);
125
	g_variant_builder_add_value(&tuple, uris);
126
	g_variant_builder_add_value(&tuple, platform);
127
128
	app_data = g_variant_builder_end(&tuple);
62.2.11 by Ted Gould
Make sure this has the lifecycle we expect
129
	g_variant_ref_sink(app_data);
62.2.10 by Ted Gould
Add in the platform data to what we give to apps
130
62.2.7 by Ted Gould
Get our URIs into something we can use
131
	return;
132
}
133
62.2.8 by Ted Gould
Get the dbus path to use for sending the message from the application name
134
/* Finds us our dbus path to use.  Basically this is the name
135
   of the application with dots replaced by / and a / tacted on
136
   the front.  This is recommended here:
137
138
   http://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#dbus   
139
*/
140
static void
141
app_id_to_dbus_path (void)
142
{
143
	if (dbus_path != NULL) {
144
		return;
145
	}
146
61.1.28 by Ted Gould
Make sure there aren't two slashes on the path
147
	dbus_path = nih_dbus_path(NULL, "", appid, NULL);
148
	g_debug("DBus Path: %s", dbus_path);
62.2.18 by Ted Gould
Use nih_dbus_path() to get the app path
149
62.2.8 by Ted Gould
Get the dbus path to use for sending the message from the application name
150
	return;
151
}
152
62.2.9 by Ted Gould
Send the open out there to the app
153
/* Finish the send and decrement the counter */
154
static void
155
send_open_cb (GObject * object, GAsyncResult * res, gpointer user_data)
156
{
157
	GError * error = NULL;
158
159
	g_dbus_connection_call_finish(G_DBUS_CONNECTION(object), res, &error);
160
161
	if (error != NULL) {
162
		/* Mostly just to free the error, but printing for debugging */
163
		g_debug("Unable to send Open: %s", error->message);
164
		g_error_free(error);
165
	}
166
167
	connection_count_dec();
168
	return;
169
}
170
62.2.6 by Ted Gould
Setup our space for callback
171
/* Sends the Open message to the connection with the URIs we were given */
172
static void
62.2.14 by Ted Gould
Changing connection variable name to be more clear
173
contact_app (GDBusConnection * bus, const gchar * dbus_name)
62.2.6 by Ted Gould
Setup our space for callback
174
{
62.2.7 by Ted Gould
Get our URIs into something we can use
175
	parse_uris();
62.2.8 by Ted Gould
Get the dbus path to use for sending the message from the application name
176
	app_id_to_dbus_path();
177
178
	/* Using the FD.o Application interface */
62.2.9 by Ted Gould
Send the open out there to the app
179
	g_dbus_connection_call(bus,
62.2.14 by Ted Gould
Changing connection variable name to be more clear
180
		dbus_name,
62.2.9 by Ted Gould
Send the open out there to the app
181
		dbus_path,
182
		"org.freedesktop.Application",
183
		"Open",
62.2.10 by Ted Gould
Add in the platform data to what we give to apps
184
		app_data,
62.2.9 by Ted Gould
Send the open out there to the app
185
		NULL,
186
		G_DBUS_CALL_FLAGS_NONE,
187
		-1,
188
		NULL,
189
		send_open_cb, NULL);
190
62.2.14 by Ted Gould
Changing connection variable name to be more clear
191
	g_debug("Sending Open request to: %s", dbus_name);
62.2.12 by Ted Gould
Handy debug message
192
62.2.6 by Ted Gould
Setup our space for callback
193
	return;
194
}
195
196
/* Gets the PID for a connection, and if it matches the one we're looking
197
   for then it tries to send a message to that connection */
62.2.4 by Ted Gould
Flesh this out a bunch so we start getting connections
198
static void
199
get_pid_cb (GObject * object, GAsyncResult * res, gpointer user_data)
200
{
62.2.14 by Ted Gould
Changing connection variable name to be more clear
201
	gchar * dbus_name = (gchar *)user_data;
62.2.4 by Ted Gould
Flesh this out a bunch so we start getting connections
202
	GError * error = NULL;
203
	GVariant * vpid = NULL;
204
205
	vpid = g_dbus_connection_call_finish(G_DBUS_CONNECTION(object), res, &error);
206
207
	if (error != NULL) {
62.2.14 by Ted Gould
Changing connection variable name to be more clear
208
		g_warning("Unable to query PID for dbus name '%s': %s", dbus_name, error->message);
62.2.4 by Ted Gould
Flesh this out a bunch so we start getting connections
209
		g_error_free(error);
62.2.14 by Ted Gould
Changing connection variable name to be more clear
210
		g_free(dbus_name);
62.2.4 by Ted Gould
Flesh this out a bunch so we start getting connections
211
212
		/* Lowering the connection count, this one is terminal, even if in error */
213
		connection_count_dec();
214
		return;
215
	}
216
217
	guint pid = 0;
218
	g_variant_get(vpid, "(u)", &pid);
219
	g_variant_unref(vpid);
220
221
	if (pid == app_pid) {
62.2.6 by Ted Gould
Setup our space for callback
222
		/* Trying to send a message to the connection */
62.2.14 by Ted Gould
Changing connection variable name to be more clear
223
		contact_app(G_DBUS_CONNECTION(object), dbus_name);
62.2.6 by Ted Gould
Setup our space for callback
224
	} else {
225
		/* See if we can quit now */
226
		connection_count_dec();
62.2.4 by Ted Gould
Flesh this out a bunch so we start getting connections
227
	}
228
62.2.14 by Ted Gould
Changing connection variable name to be more clear
229
	g_free(dbus_name);
62.2.4 by Ted Gould
Flesh this out a bunch so we start getting connections
230
231
	return;
232
}
62.2.1 by Ted Gould
Add an executable target for fdo-application-open
233
61.1.18 by Ted Gould
Pull a bunch of the PID code into it's own function
234
/* Starts to look for the PID and the connections for that PID */
235
void
61.1.20 by Ted Gould
Shoulda compiled that
236
find_appid_pid (GDBusConnection * session)
61.1.18 by Ted Gould
Pull a bunch of the PID code into it's own function
237
{
238
	GError * error = NULL;
239
240
	/* List all the connections on dbus.  This sucks that we have to do
241
	   this, but in the future we should add DBus API to do this lookup
242
	   instead of having to do it with a bunch of requests */
243
	GVariant * listnames = g_dbus_connection_call_sync(session,
244
		"org.freedesktop.DBus",
245
		"/",
246
		"org.freedesktop.DBus",
247
		"ListNames",
248
		NULL,
249
		G_VARIANT_TYPE("(as)"),
250
		G_DBUS_CALL_FLAGS_NONE,
251
		-1,
252
		NULL,
253
		&error);
254
255
	if (error != NULL) {
256
		g_warning("Unable to get list of names from DBus: %s", error->message);
257
		g_error_free(error);
258
		return;
259
	}
260
62.2.16 by Ted Gould
Move the getting of the PID to make it less racy
261
	/* Next figure out what we're looking for (and if there is something to look for) */
262
	/* NOTE: We're getting the PID *after* the list of connections so
263
	   that some new process can't come in, be the same PID as it's
264
	   connection will not be in teh list we just got. */
265
	app_pid = upstart_app_launch_get_primary_pid(appid);
266
	if (app_pid == 0) {
61.1.23 by Ted Gould
Merge the updated fdo-applications branch
267
		g_warning("Unable to find pid for app id '%s'", appid);
268
		return;
62.2.16 by Ted Gould
Move the getting of the PID to make it less racy
269
	}
270
61.1.27 by Ted Gould
Don't need a second mainloop
271
	/* Get the names */
61.1.18 by Ted Gould
Pull a bunch of the PID code into it's own function
272
	GVariant * names = g_variant_get_child_value(listnames, 0);
273
	GVariantIter iter;
274
	g_variant_iter_init(&iter, names);
275
	gchar * name = NULL;
276
277
	while (g_variant_iter_loop(&iter, "s", &name)) {
278
		/* We only want to ask each connection once, this makes that so */
279
		if (!g_dbus_is_unique_name(name)) {
280
			continue;
281
		}
282
283
		/* Get the PIDs */
284
		g_dbus_connection_call(session,
285
			"org.freedesktop.DBus",
286
			"/",
287
			"org.freedesktop.DBus",
288
			"GetConnectionUnixProcessID",
289
			g_variant_new("(s)", name),
290
			G_VARIANT_TYPE("(u)"),
291
			G_DBUS_CALL_FLAGS_NONE,
292
			-1,
293
			NULL,
294
			get_pid_cb, g_strdup(name));
295
296
		connections_open++;
297
	}
298
299
	g_variant_unref(names);
300
	g_variant_unref(listnames);
301
302
	return;
303
}
304
61.1.31 by Ted Gould
Breaking out the core of second exec to ensure we can play with it.
305
gboolean
306
second_exec (const gchar * app_id, const gchar * appuris)
62.2.1 by Ted Gould
Add an executable target for fdo-application-open
307
{
61.1.31 by Ted Gould
Breaking out the core of second exec to ensure we can play with it.
308
	appid = app_id;
309
	input_uris = appuris;
62.2.7 by Ted Gould
Get our URIs into something we can use
310
62.2.4 by Ted Gould
Flesh this out a bunch so we start getting connections
311
	/* DBus tell us! */
312
	GError * error = NULL;
313
	GDBusConnection * session = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &error);
314
	if (error != NULL) {
315
		g_error("Unable to get session bus");
316
		g_error_free(error);
61.1.31 by Ted Gould
Breaking out the core of second exec to ensure we can play with it.
317
		return FALSE;
62.2.4 by Ted Gould
Flesh this out a bunch so we start getting connections
318
	}
319
61.1.5 by Ted Gould
Setting up the callback signal from Unity to know that we've resumed
320
	/* Allocate main loop */
321
	mainloop = g_main_loop_new(NULL, FALSE);
322
61.1.4 by Ted Gould
Mostly setting up a big set of comments and TODOs to describe what we're gonna do
323
	/* Set up listening for the unfrozen signal from Unity */
61.1.5 by Ted Gould
Setting up the callback signal from Unity to know that we've resumed
324
	g_dbus_connection_signal_subscribe(session,
325
		NULL, /* sender */
326
		"com.canonical.UpstartAppLaunch", /* interface */
327
		"UnityResumeResponse", /* signal */
328
		"/", /* path */
329
		appid, /* arg0 */
330
		G_DBUS_SIGNAL_FLAGS_NONE,
331
		unity_resume_cb, mainloop,
332
		NULL); /* user data destroy */
61.1.4 by Ted Gould
Mostly setting up a big set of comments and TODOs to describe what we're gonna do
333
334
	/* Send unfreeze to to Unity */
61.1.6 by Ted Gould
Request Unity to RESUME THAT APP!
335
	g_dbus_connection_emit_signal(session,
336
		NULL, /* destination */
337
		"/", /* path */
338
		"com.canonical.UpstartAppLaunch", /* interface */
339
		"UnityResumeRequest", /* signal */
340
		g_variant_new("(s)", appid),
341
		&error);
61.1.4 by Ted Gould
Mostly setting up a big set of comments and TODOs to describe what we're gonna do
342
343
	/* Now we start a race, we try to get to the point of knowing who
344
	   to send things to, and Unity is unfrezing it.  When both are
345
	   done we can send something to the app */
346
	unity_starttime = g_get_monotonic_time();
347
61.1.6 by Ted Gould
Request Unity to RESUME THAT APP!
348
	if (error != NULL) {
349
		/* On error let's not wait for Unity */
350
		g_warning("Unable to signal Unity: %s", error->message);
351
		g_error_free(error);
352
		error = NULL;
353
		unity_starttime = 0;
354
	}
355
61.1.18 by Ted Gould
Pull a bunch of the PID code into it's own function
356
	/* If we've got something to give out, start looking for how */
61.1.20 by Ted Gould
Shoulda compiled that
357
	if (input_uris != NULL) {
358
		find_appid_pid(session);
61.1.18 by Ted Gould
Pull a bunch of the PID code into it's own function
359
	}
62.2.4 by Ted Gould
Flesh this out a bunch so we start getting connections
360
61.1.4 by Ted Gould
Mostly setting up a big set of comments and TODOs to describe what we're gonna do
361
	/* Loop and wait for everything to align */
61.1.23 by Ted Gould
Merge the updated fdo-applications branch
362
	if (connections_open > 0 || unity_starttime > 0) {
62.2.4 by Ted Gould
Flesh this out a bunch so we start getting connections
363
		g_main_loop_run(mainloop);
364
	}
61.1.18 by Ted Gould
Pull a bunch of the PID code into it's own function
365
	g_debug("Finishing main loop");
62.2.4 by Ted Gould
Flesh this out a bunch so we start getting connections
366
61.1.4 by Ted Gould
Mostly setting up a big set of comments and TODOs to describe what we're gonna do
367
	/* Now that we're done sending the info to the app, we can ask
368
	   Unity to focus the application. */
61.1.7 by Ted Gould
Focus the window!
369
	g_dbus_connection_emit_signal(session,
370
		NULL, /* destination */
371
		"/", /* path */
372
		"com.canonical.UpstartAppLaunch", /* interface */
373
		"UnityFocusRequest", /* signal */
374
		g_variant_new("(s)", appid),
375
		&error);
61.1.4 by Ted Gould
Mostly setting up a big set of comments and TODOs to describe what we're gonna do
376
61.1.7 by Ted Gould
Focus the window!
377
	if (error != NULL) {
378
		g_warning("Unable to request focus to Unity: %s", error->message);
379
		g_error_free(error);
380
		error = NULL;
381
	}
61.1.4 by Ted Gould
Mostly setting up a big set of comments and TODOs to describe what we're gonna do
382
61.1.16 by Ted Gould
Make sure the last signal hits the bus
383
	/* Make sure the signal hits the bus */
384
	g_dbus_connection_flush_sync(session, NULL, NULL);
385
61.1.4 by Ted Gould
Mostly setting up a big set of comments and TODOs to describe what we're gonna do
386
	/* Clean up */
62.2.10 by Ted Gould
Add in the platform data to what we give to apps
387
	if (app_data != NULL) {
388
		g_variant_unref(app_data);
61.1.40 by Ted Gould
Make sure we clear these variables
389
		app_data = NULL;
62.2.7 by Ted Gould
Get our URIs into something we can use
390
	}
391
62.2.4 by Ted Gould
Flesh this out a bunch so we start getting connections
392
	g_main_loop_unref(mainloop);
393
	g_object_unref(session);
61.1.26 by Ted Gould
Avoid a crash if there are no URLs
394
395
	if (dbus_path != NULL) {
396
		nih_free(dbus_path);
61.1.40 by Ted Gould
Make sure we clear these variables
397
		dbus_path = NULL;
61.1.26 by Ted Gould
Avoid a crash if there are no URLs
398
	}
62.2.1 by Ted Gould
Add an executable target for fdo-application-open
399
61.1.31 by Ted Gould
Breaking out the core of second exec to ensure we can play with it.
400
	return TRUE;
62.2.1 by Ted Gould
Add an executable target for fdo-application-open
401
}