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

« back to all changes in this revision

Viewing changes to libupstart-app-launch/upstart-app-launch.c

Updated to failure-is-an-option

Show diffs side-by-side

added added

removed removed

Lines of Context:
20
20
#include "upstart-app-launch.h"
21
21
#include <json-glib/json-glib.h>
22
22
#include <upstart.h>
23
 
#include <nih/alloc.h>
24
 
#include <nih/error.h>
25
23
#include <gio/gio.h>
26
24
#include <string.h>
27
25
 
28
 
static void apps_for_job (NihDBusProxy * upstart, const gchar * name, GArray * apps, gboolean truncate_legacy);
 
26
#include "upstart-app-launch-trace.h"
 
27
#include "second-exec-core.h"
 
28
#include "../helpers.h"
 
29
 
 
30
static void apps_for_job (GDBusConnection * con, const gchar * name, GArray * apps, gboolean truncate_legacy);
29
31
static void free_helper (gpointer value);
30
32
 
31
 
static NihDBusProxy *
32
 
nih_proxy_create (void)
33
 
{
34
 
        NihDBusProxy *   upstart;
35
 
        DBusConnection * conn;
36
 
        DBusError        error;
37
 
        const gchar *    bus_name = NULL;
38
 
 
39
 
        dbus_error_init(&error);
40
 
 
41
 
        conn = dbus_bus_get(DBUS_BUS_SESSION, &error);
42
 
        bus_name = "com.ubuntu.Upstart";
43
 
 
44
 
        if (conn == NULL) {
45
 
                g_warning("Unable to connect to the Upstart Session: %s", error.message);
46
 
                dbus_error_free(&error);
47
 
                return NULL;
48
 
        }
49
 
 
50
 
        dbus_error_free(&error);
51
 
 
52
 
        upstart = nih_dbus_proxy_new(NULL, conn,
53
 
                bus_name,
54
 
                DBUS_PATH_UPSTART,
55
 
                NULL, NULL);
56
 
 
57
 
        if (upstart == NULL) {
58
 
                g_warning("Unable to build proxy to Upstart");
59
 
                dbus_connection_unref(conn);
60
 
                return NULL;
61
 
        }
62
 
 
63
 
        dbus_connection_unref(conn);
64
 
 
65
 
        upstart->auto_start = FALSE;
66
 
 
67
 
        return upstart;
68
 
}
69
 
 
70
33
/* Function to take the urls and escape them so that they can be
71
34
   parsed on the other side correctly. */
72
35
static gchar *
87
50
        return urisjoin;
88
51
}
89
52
 
 
53
typedef struct {
 
54
        gchar * appid;
 
55
        gchar * uris;
 
56
} app_start_t;
 
57
 
 
58
static void
 
59
application_start_cb (GObject * obj, GAsyncResult * res, gpointer user_data)
 
60
{
 
61
        app_start_t * data = (app_start_t *)user_data;
 
62
        GError * error = NULL;
 
63
        GVariant * result = NULL;
 
64
 
 
65
        tracepoint(upstart_app_launch, libual_start_message_callback, data->appid);
 
66
        g_debug("Started Message Callback: %s", data->appid);
 
67
 
 
68
        result = g_dbus_connection_call_finish(G_DBUS_CONNECTION(obj), res, &error);
 
69
 
 
70
        if (result != NULL)
 
71
                g_variant_unref(result);
 
72
        
 
73
        if (error != NULL) {
 
74
                if (g_dbus_error_is_remote_error(error)) {
 
75
                        gchar * remote_error = g_dbus_error_get_remote_error(error);
 
76
                        g_debug("Remote error: %s", remote_error);
 
77
                        if (g_strcmp0(remote_error, "com.ubuntu.Upstart0_6.Error.AlreadyStarted") == 0) {
 
78
                                second_exec(data->appid, data->uris);
 
79
                        }
 
80
                } else {
 
81
                        g_warning("Unable to emit event to start application: %s", error->message);
 
82
                }
 
83
                g_error_free(error);
 
84
        }
 
85
 
 
86
        g_free(data->appid);
 
87
        g_free(data->uris);
 
88
        g_free(data);
 
89
}
 
90
 
 
91
/* Get the path of the job from Upstart, if we've got it already, we'll just
 
92
   use the cache of the value */
 
93
const gchar *
 
94
get_jobpath (GDBusConnection * con, const gchar * jobname)
 
95
{
 
96
        gchar * cachepath = g_strdup_printf("upstart-app-lauch-job-path-cache-%s", jobname);
 
97
        gpointer cachedata = g_object_get_data(G_OBJECT(con), cachepath);
 
98
 
 
99
        if (cachedata != NULL) {
 
100
                g_free(cachepath);
 
101
                return cachedata;
 
102
        }
 
103
 
 
104
        GError * error = NULL;
 
105
        GVariant * job_path_variant = g_dbus_connection_call_sync(con,
 
106
                DBUS_SERVICE_UPSTART,
 
107
                DBUS_PATH_UPSTART,
 
108
                DBUS_INTERFACE_UPSTART,
 
109
                "GetJobByName",
 
110
                g_variant_new("(s)", jobname),
 
111
                G_VARIANT_TYPE("(o)"),
 
112
                G_DBUS_CALL_FLAGS_NONE,
 
113
                -1, /* timeout: default */
 
114
                NULL, /* cancelable */
 
115
                &error);
 
116
 
 
117
        if (error != NULL) {    
 
118
                g_warning("Unable to find job '%s': %s", jobname, error->message);
 
119
                g_error_free(error);
 
120
                g_free(cachepath);
 
121
                return NULL;
 
122
        }
 
123
 
 
124
        gchar * job_path = NULL;
 
125
        g_variant_get(job_path_variant, "(o)", &job_path);
 
126
        g_variant_unref(job_path_variant);
 
127
 
 
128
        g_object_set_data_full(G_OBJECT(con), cachepath, job_path, g_free);
 
129
        g_free(cachepath);
 
130
 
 
131
        return job_path;
 
132
}
 
133
 
 
134
/* Check to see if a legacy app wants us to manage whether they're
 
135
   single instance or not */
 
136
gboolean
 
137
legacy_single_instance (const gchar * appid)
 
138
{
 
139
        tracepoint(upstart_app_launch, desktop_single_start, appid);
 
140
 
 
141
        GKeyFile * keyfile = keyfile_for_appid(appid, NULL);
 
142
 
 
143
        if (keyfile == NULL) {
 
144
                g_error("Unable to find keyfile for application '%s'", appid);
 
145
                return FALSE;
 
146
        }
 
147
 
 
148
        tracepoint(upstart_app_launch, desktop_single_found, appid);
 
149
 
 
150
        gboolean singleinstance = FALSE;
 
151
 
 
152
        if (g_key_file_has_key(keyfile, "Desktop Entry", "X-Ubuntu-Single-Instance", NULL)) {
 
153
                GError * error = NULL;
 
154
 
 
155
                singleinstance = g_key_file_get_boolean(keyfile, "Desktop Entry", "X-Ubuntu-Single-Instance", &error);
 
156
 
 
157
                if (error != NULL) {
 
158
                        g_warning("Unable to get single instance key for app '%s': %s", appid, error->message);
 
159
                        g_error_free(error);
 
160
                        /* Ensure that if we got an error, we assume standard case */
 
161
                        singleinstance = FALSE;
 
162
                }
 
163
        }
 
164
        
 
165
        g_key_file_free(keyfile);
 
166
 
 
167
        tracepoint(upstart_app_launch, desktop_single_finished, appid, singleinstance ? "single" : "unmanaged");
 
168
 
 
169
        return singleinstance;
 
170
}
 
171
 
90
172
gboolean
91
173
upstart_app_launch_start_application (const gchar * appid, const gchar * const * uris)
92
174
{
93
 
        NihDBusProxy * proxy = NULL;
94
 
 
95
 
        proxy = nih_proxy_create();
96
 
        if (proxy == NULL) {
 
175
        g_return_val_if_fail(appid != NULL, FALSE);
 
176
 
 
177
        tracepoint(upstart_app_launch, libual_start, appid);
 
178
 
 
179
        GDBusConnection * con = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
 
180
        g_return_val_if_fail(con != NULL, FALSE);
 
181
 
 
182
        /* Determine whether it's a click package by looking for the symlink
 
183
           that is created by the desktop hook */
 
184
        gchar * appiddesktop = g_strdup_printf("%s.desktop", appid);
 
185
        gchar * click_link = NULL;
 
186
        const gchar * link_farm_dir = g_getenv("UPSTART_APP_LAUNCH_LINK_FARM");
 
187
        if (G_LIKELY(link_farm_dir == NULL)) {
 
188
                click_link = g_build_filename(g_get_home_dir(), ".cache", "upstart-app-launch", "desktop", appiddesktop, NULL);
 
189
        } else {
 
190
                click_link = g_build_filename(link_farm_dir, appiddesktop, NULL);
 
191
        }
 
192
        g_free(appiddesktop);
 
193
        gboolean click = g_file_test(click_link, G_FILE_TEST_EXISTS);
 
194
        g_free(click_link);
 
195
 
 
196
        tracepoint(upstart_app_launch, libual_determine_type, appid, click ? "click" : "legacy");
 
197
 
 
198
        /* Figure out the DBus path for the job */
 
199
        const gchar * jobpath = NULL;
 
200
        if (click) {
 
201
                jobpath = get_jobpath(con, "application-click");
 
202
        } else {
 
203
                jobpath = get_jobpath(con, "application-legacy");
 
204
        }
 
205
 
 
206
        if (jobpath == NULL)
97
207
                return FALSE;
98
 
        }
99
 
 
100
 
        gchar * env_appid = g_strdup_printf("APP_ID=%s", appid);
101
 
        gchar * env_uris = NULL;
 
208
 
 
209
        tracepoint(upstart_app_launch, libual_job_path_determined, appid, jobpath);
 
210
 
 
211
        /* Callback data */
 
212
        app_start_t * app_start_data = g_new0(app_start_t, 1);
 
213
        app_start_data->appid = g_strdup(appid);
 
214
 
 
215
        /* Build up our environment */
 
216
        GVariantBuilder builder;
 
217
        g_variant_builder_init(&builder, G_VARIANT_TYPE_TUPLE);
 
218
 
 
219
        g_variant_builder_open(&builder, G_VARIANT_TYPE_ARRAY);
 
220
 
 
221
        g_variant_builder_add_value(&builder, g_variant_new_take_string(g_strdup_printf("APP_ID=%s", appid)));
102
222
 
103
223
        if (uris != NULL) {
104
224
                gchar * urisjoin = app_uris_string(uris);
105
 
                env_uris = g_strdup_printf("APP_URIS=%s", urisjoin);
106
 
                g_free(urisjoin);
107
 
        }
108
 
 
109
 
        gchar * env[3];
110
 
        env[0] = env_appid;
111
 
        env[1] = env_uris;
112
 
        env[2] = NULL;
113
 
 
114
 
        gboolean retval = TRUE;
115
 
        if (upstart_emit_event_sync(NULL, proxy, "application-start", env, 0) != 0) {
116
 
                g_warning("Unable to emit signal 'application-start'");
117
 
                retval = FALSE;
118
 
        }
119
 
 
120
 
        g_free(env_appid);
121
 
        g_free(env_uris);
122
 
        nih_unref(proxy, NULL);
123
 
 
124
 
        return retval;
 
225
                gchar * urienv = g_strdup_printf("APP_URIS=%s", urisjoin);
 
226
                app_start_data->uris = urisjoin;
 
227
                g_variant_builder_add_value(&builder, g_variant_new_take_string(urienv));
 
228
        }
 
229
 
 
230
        if (!click) {
 
231
                if (legacy_single_instance(appid)) {
 
232
                        g_variant_builder_add_value(&builder, g_variant_new_string("INSTANCE_ID="));
 
233
                } else {
 
234
                        gchar * instanceid = g_strdup_printf("INSTANCE_ID=%" G_GUINT64_FORMAT, g_get_real_time());
 
235
                        g_variant_builder_add_value(&builder, g_variant_new_take_string(instanceid));
 
236
                }
 
237
        }
 
238
 
 
239
        g_variant_builder_close(&builder);
 
240
        g_variant_builder_add_value(&builder, g_variant_new_boolean(TRUE));
 
241
        
 
242
        /* Call the job start function */
 
243
        g_dbus_connection_call(con,
 
244
                               DBUS_SERVICE_UPSTART,
 
245
                               jobpath,
 
246
                               DBUS_INTERFACE_UPSTART_JOB,
 
247
                               "Start",
 
248
                               g_variant_builder_end(&builder),
 
249
                               NULL,
 
250
                               G_DBUS_CALL_FLAGS_NONE,
 
251
                               -1,
 
252
                               NULL, /* cancelable */
 
253
                               application_start_cb,
 
254
                               app_start_data);
 
255
 
 
256
        tracepoint(upstart_app_launch, libual_start_message_sent, appid);
 
257
 
 
258
        g_object_unref(con);
 
259
 
 
260
        return TRUE;
125
261
}
126
262
 
127
263
static void
128
 
stop_job (NihDBusProxy * upstart, const gchar * jobname, const gchar * appname, const gchar * instanceid)
 
264
stop_job (GDBusConnection * con, const gchar * jobname, const gchar * appname, const gchar * instanceid)
129
265
{
130
266
        g_debug("Stopping job %s app_id %s instance_id %s", jobname, appname, instanceid);
131
 
        nih_local char * job_path = NULL;
132
 
        if (upstart_get_job_by_name_sync(NULL, upstart, jobname, &job_path) != 0) {
133
 
                g_warning("Unable to find job '%s'", jobname);
134
 
                return;
135
 
        }
136
 
 
137
 
        NihDBusProxy * job_proxy = nih_dbus_proxy_new(NULL, upstart->connection,
138
 
                upstart->name,
139
 
                job_path,
140
 
                NULL, NULL);
141
 
 
142
 
        if (job_proxy == NULL) {
143
 
                g_warning("Unable to build proxy to Job '%s'", jobname);
144
 
                return;
145
 
        }
146
 
 
147
 
        gchar * app = g_strdup_printf("APP_ID=%s", appname);
148
 
        gchar * inst = NULL;
 
267
 
 
268
        const gchar * job_path = get_jobpath(con, jobname);
 
269
        if (job_path == NULL)
 
270
                return;
 
271
 
 
272
        GVariantBuilder builder;
 
273
        g_variant_builder_init(&builder, G_VARIANT_TYPE_TUPLE);
 
274
        g_variant_builder_open(&builder, G_VARIANT_TYPE_ARRAY);
 
275
 
 
276
        g_variant_builder_add_value(&builder,
 
277
                g_variant_new_take_string(g_strdup_printf("APP_ID=%s", appname)));
149
278
        
150
279
        if (instanceid != NULL) {
151
 
                inst = g_strdup_printf("INSTANCE_ID=%s", instanceid);
152
 
        }
153
 
 
154
 
        gchar * env[3] = {
155
 
                app,
156
 
                inst,
157
 
                NULL
158
 
        };
159
 
 
160
 
        if (job_class_stop_sync(NULL, job_proxy, env, 0) != 0) {
161
 
                g_warning("Unable to stop job %s app %s instance %s", jobname, appname, instanceid);
162
 
        }
163
 
 
164
 
        g_free(app);
165
 
        g_free(inst);
166
 
        nih_unref(job_proxy, NULL);
 
280
                g_variant_builder_add_value(&builder,
 
281
                        g_variant_new_take_string(g_strdup_printf("INSTANCE_ID=%s", instanceid)));
 
282
        }
 
283
 
 
284
        g_variant_builder_close(&builder);
 
285
        g_variant_builder_add_value(&builder, g_variant_new_boolean(FALSE)); /* wait */
 
286
 
 
287
        GError * error = NULL;
 
288
        GVariant * stop_variant = g_dbus_connection_call_sync(con,
 
289
                DBUS_SERVICE_UPSTART,
 
290
                job_path,
 
291
                DBUS_INTERFACE_UPSTART_JOB,
 
292
                "Stop",
 
293
                g_variant_builder_end(&builder),
 
294
                NULL,
 
295
                G_DBUS_CALL_FLAGS_NONE,
 
296
                -1, /* timeout: default */
 
297
                NULL, /* cancelable */
 
298
                &error);
 
299
 
 
300
        if (error != NULL) {
 
301
                g_warning("Unable to stop job %s app_id %s instance_id %s: %s", jobname, appname, instanceid, error->message);
 
302
                g_error_free(error);
 
303
        }
 
304
 
 
305
        g_variant_unref(stop_variant);
167
306
}
168
307
 
169
308
static void
176
315
gboolean
177
316
upstart_app_launch_stop_application (const gchar * appid)
178
317
{
 
318
        g_return_val_if_fail(appid != NULL, FALSE);
 
319
 
 
320
        GDBusConnection * con = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
 
321
        g_return_val_if_fail(con != NULL, FALSE);
 
322
 
179
323
        gboolean found = FALSE;
180
324
        int i;
181
 
        NihDBusProxy * proxy = NULL;
182
 
 
183
 
        proxy = nih_proxy_create();
184
 
        if (proxy == NULL) {
185
 
                return FALSE;
186
 
        }
187
325
 
188
326
        GArray * apps = g_array_new(TRUE, TRUE, sizeof(gchar *));
189
327
        g_array_set_clear_func(apps, free_helper);
190
328
 
191
329
        /* Look through the click jobs and see if any match.  There can
192
330
           only be one instance for each ID in the click world */
193
 
        apps_for_job(proxy, "application-click", apps, FALSE);
 
331
        apps_for_job(con, "application-click", apps, FALSE);
194
332
        for (i = 0; i < apps->len; i++) {
195
333
                const gchar * array_id = g_array_index(apps, const gchar *, i);
196
334
                if (g_strcmp0(array_id, appid) == 0) {
197
 
                        stop_job(proxy, "application-click", appid, NULL);
 
335
                        stop_job(con, "application-click", appid, NULL);
198
336
                        found = TRUE;
199
337
                        break; /* There can be only one with click */
200
338
                }
206
344
        /* Look through the legacy apps.  Trickier because we know that there
207
345
           can be many instances of the legacy jobs out there, so we might
208
346
           have to kill more than one of them. */
209
 
        apps_for_job(proxy, "application-legacy", apps, FALSE);
 
347
        apps_for_job(con, "application-legacy", apps, FALSE);
210
348
        gchar * appiddash = g_strdup_printf("%s-", appid); /* Probably could go RegEx here, but let's start with just a prefix lookup */
211
349
        for (i = 0; i < apps->len; i++) {
212
350
                const gchar * array_id = g_array_index(apps, const gchar *, i);
213
351
                if (g_str_has_prefix(array_id, appiddash)) {
214
352
                        gchar * instanceid = g_strrstr(array_id, "-");
215
 
                        stop_job(proxy, "application-legacy", appid, &(instanceid[1]));
 
353
                        stop_job(con, "application-legacy", appid, &(instanceid[1]));
216
354
                        found = TRUE;
217
355
                }
218
356
        }
219
357
        g_free(appiddash);
220
358
 
221
359
        g_array_free(apps, TRUE);
222
 
        nih_unref(proxy, NULL);
 
360
        g_object_unref(con);
223
361
 
224
362
        return found;
225
363
}
255
393
        gpointer user_data;
256
394
};
257
395
 
 
396
/* The data we keep for each failed observer */
 
397
typedef struct _failed_observer_t failed_observer_t;
 
398
struct _failed_observer_t {
 
399
        GDBusConnection * conn;
 
400
        guint sighandle;
 
401
        upstart_app_launch_app_failed_observer_t func;
 
402
        gpointer user_data;
 
403
};
 
404
 
258
405
/* The lists of Observers */
259
406
static GList * starting_array = NULL;
260
407
static GList * started_array = NULL;
261
408
static GList * stop_array = NULL;
262
409
static GList * focus_array = NULL;
263
410
static GList * resume_array = NULL;
 
411
static GList * failed_array = NULL;
264
412
 
265
413
static void
266
414
observer_cb (GDBusConnection * conn, const gchar * sender, const gchar * object, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data)
267
415
{
268
416
        observer_t * observer = (observer_t *)user_data;
269
417
 
 
418
        const gchar * signalname = NULL;
 
419
        g_variant_get_child(params, 0, "&s", &signalname);
 
420
 
 
421
        tracepoint(upstart_app_launch, observer_start, signalname);
 
422
 
270
423
        gchar * env = NULL;
271
424
        GVariant * envs = g_variant_get_child_value(params, 1);
272
425
        GVariantIter iter;
300
453
                observer->func(instance, observer->user_data);
301
454
        }
302
455
 
 
456
        tracepoint(upstart_app_launch, observer_finish, signalname);
 
457
 
303
458
        g_free(instance);
304
459
}
305
460
 
388
543
        observer_t * observer = (observer_t *)user_data;
389
544
        const gchar * appid = NULL;
390
545
 
 
546
        tracepoint(upstart_app_launch, observer_start, "focus");
 
547
 
391
548
        if (observer->func != NULL) {
392
549
                g_variant_get(params, "(&s)", &appid);
393
550
                observer->func(appid, observer->user_data);
394
551
        }
 
552
 
 
553
        tracepoint(upstart_app_launch, observer_finish, "focus");
395
554
}
396
555
 
397
556
gboolean
404
563
static void
405
564
resume_signal_cb (GDBusConnection * conn, const gchar * sender, const gchar * object, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data)
406
565
{
 
566
        tracepoint(upstart_app_launch, observer_start, "resume");
 
567
 
407
568
        focus_signal_cb(conn, sender, object, interface, signal, params, user_data);
408
569
 
409
570
        GError * error = NULL;
419
580
                g_warning("Unable to emit response signal: %s", error->message);
420
581
                g_error_free(error);
421
582
        }
 
583
 
 
584
        tracepoint(upstart_app_launch, observer_finish, "resume");
422
585
}
423
586
 
424
587
gboolean
431
594
static void
432
595
starting_signal_cb (GDBusConnection * conn, const gchar * sender, const gchar * object, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data)
433
596
{
 
597
        tracepoint(upstart_app_launch, observer_start, "starting");
 
598
 
434
599
        focus_signal_cb(conn, sender, object, interface, signal, params, user_data);
435
600
 
436
601
        GError * error = NULL;
446
611
                g_warning("Unable to emit response signal: %s", error->message);
447
612
                g_error_free(error);
448
613
        }
 
614
 
 
615
        tracepoint(upstart_app_launch, observer_finish, "starting");
449
616
}
450
617
 
451
618
gboolean
454
621
        return add_session_generic(observer, user_data, "UnityStartingBroadcast", &starting_array, starting_signal_cb);
455
622
}
456
623
 
 
624
/* Handle the failed signal when it occurs, call the observer */
 
625
static void
 
626
failed_signal_cb (GDBusConnection * conn, const gchar * sender, const gchar * object, const gchar * interface, const gchar * signal, GVariant * params, gpointer user_data)
 
627
{
 
628
        failed_observer_t * observer = (failed_observer_t *)user_data;
 
629
        const gchar * appid = NULL;
 
630
        const gchar * typestr = NULL;
 
631
 
 
632
        tracepoint(upstart_app_launch, observer_start, "failed");
 
633
 
 
634
        if (observer->func != NULL) {
 
635
                upstart_app_launch_app_failed_t type = UPSTART_APP_LAUNCH_APP_FAILED_CRASH;
 
636
                g_variant_get(params, "(&s&s)", &appid, &typestr);
 
637
 
 
638
                if (g_strcmp0("crash", typestr) == 0) {
 
639
                        type = UPSTART_APP_LAUNCH_APP_FAILED_CRASH;
 
640
                } else if (g_strcmp0("start-failure", typestr) == 0) {
 
641
                        type = UPSTART_APP_LAUNCH_APP_FAILED_START_FAILURE;
 
642
                } else {
 
643
                        g_warning("Application failure type '%s' unknown, reporting as a crash", typestr);
 
644
                }
 
645
 
 
646
                observer->func(appid, type, observer->user_data);
 
647
        }
 
648
 
 
649
        tracepoint(upstart_app_launch, observer_finish, "failed");
 
650
}
 
651
 
457
652
gboolean
458
653
upstart_app_launch_observer_add_app_failed (upstart_app_launch_app_failed_observer_t observer, gpointer user_data)
459
654
{
460
 
        return FALSE;
 
655
        GDBusConnection * conn = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
 
656
 
 
657
        if (conn == NULL) {
 
658
                return FALSE;
 
659
        }
 
660
 
 
661
        failed_observer_t * observert = g_new0(failed_observer_t, 1);
 
662
 
 
663
        observert->conn = conn;
 
664
        observert->func = observer;
 
665
        observert->user_data = user_data;
 
666
 
 
667
        failed_array = g_list_prepend(failed_array, observert);
 
668
 
 
669
        observert->sighandle = g_dbus_connection_signal_subscribe(conn,
 
670
                NULL, /* sender */
 
671
                "com.canonical.UpstartAppLaunch", /* interface */
 
672
                "ApplicationFailed", /* signal */
 
673
                "/", /* path */
 
674
                NULL, /* arg0 */
 
675
                G_DBUS_SIGNAL_FLAGS_NONE,
 
676
                failed_signal_cb,
 
677
                observert,
 
678
                NULL); /* user data destroy */
 
679
 
 
680
        return TRUE;
461
681
}
462
682
 
463
683
static gboolean
520
740
gboolean
521
741
upstart_app_launch_observer_delete_app_failed (upstart_app_launch_app_failed_observer_t observer, gpointer user_data)
522
742
{
523
 
        return FALSE;
 
743
        failed_observer_t * observert = NULL;
 
744
        GList * look;
 
745
 
 
746
        for (look = failed_array; look != NULL; look = g_list_next(look)) {
 
747
                observert = (failed_observer_t *)look->data;
 
748
 
 
749
                if (observert->func == observer && observert->user_data == user_data) {
 
750
                        break;
 
751
                }
 
752
        }
 
753
 
 
754
        if (look == NULL) {
 
755
                return FALSE;
 
756
        }
 
757
 
 
758
        g_dbus_connection_signal_unsubscribe(observert->conn, observert->sighandle);
 
759
        g_object_unref(observert->conn);
 
760
 
 
761
        g_free(observert);
 
762
        failed_array = g_list_delete_link(failed_array, look);
 
763
 
 
764
        return TRUE;
 
765
}
 
766
 
 
767
typedef void (*per_instance_func_t) (GDBusConnection * con, GVariant * prop_dict, gpointer user_data);
 
768
 
 
769
static void
 
770
foreach_job_instance (GDBusConnection * con, const gchar * jobname, per_instance_func_t func, gpointer user_data)
 
771
{
 
772
        const gchar * job_path = get_jobpath(con, jobname);
 
773
        if (job_path == NULL)
 
774
                return;
 
775
 
 
776
        GError * error = NULL;
 
777
        GVariant * instance_tuple = g_dbus_connection_call_sync(con,
 
778
                DBUS_SERVICE_UPSTART,
 
779
                job_path,
 
780
                DBUS_INTERFACE_UPSTART_JOB,
 
781
                "GetAllInstances",
 
782
                NULL,
 
783
                G_VARIANT_TYPE("(ao)"),
 
784
                G_DBUS_CALL_FLAGS_NONE,
 
785
                -1, /* timeout: default */
 
786
                NULL, /* cancelable */
 
787
                &error);
 
788
 
 
789
        if (error != NULL) {
 
790
                g_warning("Unable to get instances of job '%s': %s", jobname, error->message);
 
791
                g_error_free(error);
 
792
                return;
 
793
        }
 
794
 
 
795
        GVariant * instance_list = g_variant_get_child_value(instance_tuple, 0);
 
796
        g_variant_unref(instance_tuple);
 
797
 
 
798
        GVariantIter instance_iter;
 
799
        g_variant_iter_init(&instance_iter, instance_list);
 
800
        const gchar * instance_path = NULL;
 
801
 
 
802
        while (g_variant_iter_loop(&instance_iter, "&o", &instance_path)) {
 
803
                GVariant * props_tuple = g_dbus_connection_call_sync(con,
 
804
                        DBUS_SERVICE_UPSTART,
 
805
                        instance_path,
 
806
                        "org.freedesktop.DBus.Properties",
 
807
                        "GetAll",
 
808
                        g_variant_new("(s)", DBUS_INTERFACE_UPSTART_INSTANCE),
 
809
                        G_VARIANT_TYPE("(a{sv})"),
 
810
                        G_DBUS_CALL_FLAGS_NONE,
 
811
                        -1, /* timeout: default */
 
812
                        NULL, /* cancelable */
 
813
                        &error);
 
814
 
 
815
                if (error != NULL) {
 
816
                        g_warning("Unable to name of instance '%s': %s", instance_path, error->message);
 
817
                        g_error_free(error);
 
818
                        error = NULL;
 
819
                        continue;
 
820
                }
 
821
 
 
822
                GVariant * props_dict = g_variant_get_child_value(props_tuple, 0);
 
823
 
 
824
                func(con, props_dict, user_data);
 
825
 
 
826
                g_variant_unref(props_dict);
 
827
                g_variant_unref(props_tuple);
 
828
 
 
829
        }
 
830
 
 
831
        g_variant_unref(instance_list);
 
832
}
 
833
 
 
834
typedef struct {
 
835
        GArray * apps;
 
836
        gboolean truncate_legacy;
 
837
        const gchar * jobname;
 
838
} apps_for_job_t;
 
839
 
 
840
static void
 
841
apps_for_job_instance (GDBusConnection * con, GVariant * props_dict, gpointer user_data)
 
842
{
 
843
        GVariant * namev = g_variant_lookup_value(props_dict, "name", G_VARIANT_TYPE_STRING);
 
844
        if (namev == NULL) {
 
845
                return;
 
846
        }
 
847
 
 
848
        apps_for_job_t * data = (apps_for_job_t *)user_data;
 
849
        gchar * instance_name = g_variant_dup_string(namev, NULL);
 
850
        g_variant_unref(namev);
 
851
 
 
852
        if (data->truncate_legacy && g_strcmp0(data->jobname, "application-legacy") == 0) {
 
853
                gchar * last_dash = g_strrstr(instance_name, "-");
 
854
                if (last_dash != NULL) {
 
855
                        last_dash[0] = '\0';
 
856
                }
 
857
        }
 
858
 
 
859
        g_array_append_val(data->apps, instance_name);
524
860
}
525
861
 
526
862
/* Get all the instances for a given job name */
527
863
static void
528
 
apps_for_job (NihDBusProxy * upstart, const gchar * name, GArray * apps, gboolean truncate_legacy)
 
864
apps_for_job (GDBusConnection * con, const gchar * jobname, GArray * apps, gboolean truncate_legacy)
529
865
{
530
 
        nih_local char * job_path = NULL;
531
 
        if (upstart_get_job_by_name_sync(NULL, upstart, name, &job_path) != 0) {
532
 
                g_warning("Unable to find job '%s'", name);
533
 
                return;
534
 
        }
535
 
 
536
 
        nih_local NihDBusProxy * job_proxy = nih_dbus_proxy_new(NULL, upstart->connection,
537
 
                upstart->name,
538
 
                job_path,
539
 
                NULL, NULL);
540
 
 
541
 
        if (job_proxy == NULL) {
542
 
                g_warning("Unable to build proxy to Job '%s'", name);
543
 
                return;
544
 
        }
545
 
 
546
 
        nih_local char ** instances;
547
 
        if (job_class_get_all_instances_sync(NULL, job_proxy, &instances) != 0) {
548
 
                NihError * error = nih_error_get();
549
 
                g_warning("Unable to get instances for job '%s': %s", name, error->message);
550
 
                nih_free(error);
551
 
                return;
552
 
        }
553
 
 
554
 
        int jobnum;
555
 
        for (jobnum = 0; instances[jobnum] != NULL; jobnum++) {
556
 
                NihDBusProxy * instance_proxy = nih_dbus_proxy_new(NULL, upstart->connection,
557
 
                        upstart->name,
558
 
                        instances[jobnum],
559
 
                        NULL, NULL);
560
 
 
561
 
                nih_local char * instance_name = NULL;
562
 
                if (job_get_name_sync(NULL, instance_proxy, &instance_name) == 0) {
563
 
                        gchar * dup = g_strdup(instance_name);
564
 
 
565
 
                        if (truncate_legacy && g_strcmp0(name, "application-legacy") == 0) {
566
 
                                gchar * last_dash = g_strrstr(dup, "-");
567
 
                                if (last_dash != NULL) {
568
 
                                        last_dash[0] = '\0';
569
 
                                }
570
 
                        }
571
 
 
572
 
                        g_array_append_val(apps, dup);
573
 
                } else {
574
 
                        g_warning("Unable to get name for instance '%s' of job '%s'", instances[jobnum], name);
575
 
                }
576
 
 
577
 
                nih_unref(instance_proxy, NULL);
578
 
        }
 
866
        apps_for_job_t data = {
 
867
                .jobname = jobname,
 
868
                .apps = apps,
 
869
                .truncate_legacy = truncate_legacy
 
870
        };
 
871
 
 
872
        foreach_job_instance(con, jobname, apps_for_job_instance, &data);
579
873
}
580
874
 
581
875
gchar **
582
876
upstart_app_launch_list_running_apps (void)
583
877
{
584
 
        NihDBusProxy * proxy = NULL;
585
 
 
586
 
        proxy = nih_proxy_create();
587
 
        if (proxy == NULL) {
588
 
                return g_new0(gchar *, 1);
589
 
        }
 
878
        GDBusConnection * con = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
 
879
        g_return_val_if_fail(con != NULL, g_new0(gchar *, 1));
590
880
 
591
881
        GArray * apps = g_array_new(TRUE, TRUE, sizeof(gchar *));
592
882
 
593
 
        apps_for_job(proxy, "application-legacy", apps, TRUE);
594
 
        apps_for_job(proxy, "application-click", apps, FALSE);
 
883
        apps_for_job(con, "application-legacy", apps, TRUE);
 
884
        apps_for_job(con, "application-click", apps, FALSE);
595
885
 
596
 
        nih_unref(proxy, NULL);
 
886
        g_object_unref(con);
597
887
 
598
888
        return (gchar **)g_array_free(apps, FALSE);
599
889
}
600
890
 
 
891
typedef struct {
 
892
        GPid pid;
 
893
        const gchar * appid;
 
894
        const gchar * jobname;
 
895
} pid_for_job_t;
 
896
 
 
897
static void
 
898
pid_for_job_instance (GDBusConnection * con, GVariant * props_dict, gpointer user_data)
 
899
{
 
900
        GVariant * namev = g_variant_lookup_value(props_dict, "name", G_VARIANT_TYPE_STRING);
 
901
        if (namev == NULL) {
 
902
                return;
 
903
        }
 
904
 
 
905
        pid_for_job_t * data = (pid_for_job_t *)user_data;
 
906
        gchar * instance_name = g_variant_dup_string(namev, NULL);
 
907
        g_variant_unref(namev);
 
908
 
 
909
        if (g_strcmp0(data->jobname, "application-legacy") == 0) {
 
910
                gchar * last_dash = g_strrstr(instance_name, "-");
 
911
                if (last_dash != NULL) {
 
912
                        last_dash[0] = '\0';
 
913
                }
 
914
        }
 
915
 
 
916
        if (g_strcmp0(instance_name, data->appid) == 0) {
 
917
                GVariant * processv = g_variant_lookup_value(props_dict, "processes", G_VARIANT_TYPE("a(si)"));
 
918
 
 
919
                if (processv != NULL) {
 
920
                        if (g_variant_n_children(processv) > 0) {
 
921
                                GVariant * first_entry = g_variant_get_child_value(processv, 0);
 
922
                                GVariant * pidv = g_variant_get_child_value(first_entry, 1);
 
923
 
 
924
                                data->pid = g_variant_get_int32(pidv);
 
925
 
 
926
                                g_variant_unref(pidv);
 
927
                                g_variant_unref(first_entry);
 
928
                        }
 
929
 
 
930
                        g_variant_unref(processv);
 
931
                }
 
932
        }
 
933
 
 
934
        g_free(instance_name);
 
935
}
 
936
 
601
937
/* Look for the app for a job */
602
938
static GPid
603
 
pid_for_job (NihDBusProxy * upstart, const gchar * job, const gchar * appid)
 
939
pid_for_job (GDBusConnection * con, const gchar * jobname, const gchar * appid)
604
940
{
605
 
        nih_local char * job_path = NULL;
606
 
        if (upstart_get_job_by_name_sync(NULL, upstart, job, &job_path) != 0) {
607
 
                g_warning("Unable to find job '%s'", job);
608
 
                return 0;
609
 
        }
610
 
 
611
 
        NihDBusProxy * job_proxy = nih_dbus_proxy_new(NULL, upstart->connection,
612
 
                upstart->name,
613
 
                job_path,
614
 
                NULL, NULL);
615
 
 
616
 
        if (job_proxy == NULL) {
617
 
                g_warning("Unable to build proxy to Job '%s'", job);
618
 
                return 0;
619
 
        }
620
 
 
621
 
        nih_local char ** instances;
622
 
        if (job_class_get_all_instances_sync(NULL, job_proxy, &instances) != 0) {
623
 
                g_warning("Unable to get instances for job '%s'", job);
624
 
                nih_unref(job_proxy, NULL);
625
 
                return 0;
626
 
        }
627
 
 
628
 
        GPid pid = 0;
629
 
        int jobnum;
630
 
        for (jobnum = 0; instances[jobnum] != NULL && pid == 0; jobnum++) {
631
 
                NihDBusProxy * instance_proxy = nih_dbus_proxy_new(NULL, upstart->connection,
632
 
                        upstart->name,
633
 
                        instances[jobnum],
634
 
                        NULL, NULL);
635
 
 
636
 
                nih_local char * instance_name = NULL;
637
 
                if (job_get_name_sync(NULL, instance_proxy, &instance_name) == 0) {
638
 
                        if (g_strcmp0(job, "application-legacy") == 0) {
639
 
                                gchar * last_dash = g_strrstr(instance_name, "-");
640
 
                                if (last_dash != NULL) {
641
 
                                        last_dash[0] = '\0';
642
 
                                }
643
 
                        }
644
 
                } else {
645
 
                        g_warning("Unable to get name for instance '%s' of job '%s'", instances[jobnum], job);
646
 
                }
647
 
 
648
 
                if (g_strcmp0(instance_name, appid) == 0) {
649
 
                        nih_local JobProcessesElement ** elements;
650
 
                        if (job_get_processes_sync(NULL, instance_proxy, &elements) == 0) {
651
 
                                pid = elements[0]->item1;
652
 
                        }
653
 
                }
654
 
 
655
 
                nih_unref(instance_proxy, NULL);
656
 
        }
657
 
 
658
 
        nih_unref(job_proxy, NULL);
659
 
 
660
 
        return pid;
 
941
        pid_for_job_t data = {
 
942
                .jobname = jobname,
 
943
                .appid = appid,
 
944
                .pid = 0
 
945
        };
 
946
 
 
947
        foreach_job_instance(con, jobname, pid_for_job_instance, &data);
 
948
 
 
949
        return data.pid;
661
950
}
662
951
 
663
952
GPid
664
953
upstart_app_launch_get_primary_pid (const gchar * appid)
665
954
{
666
 
        NihDBusProxy * proxy = NULL;
 
955
        g_return_val_if_fail(appid != NULL, 0);
667
956
 
668
 
        proxy = nih_proxy_create();
669
 
        if (proxy == NULL) {
670
 
                return 0;
671
 
        }
 
957
        GDBusConnection * con = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
 
958
        g_return_val_if_fail(con != NULL, 0);
672
959
 
673
960
        GPid pid = 0;
674
961
 
675
962
        if (pid == 0) {
676
 
                pid = pid_for_job(proxy, "application-legacy", appid);
 
963
                pid = pid_for_job(con, "application-legacy", appid);
677
964
        }
678
965
 
679
966
        if (pid == 0) {
680
 
                pid = pid_for_job(proxy, "application-click", appid);
 
967
                pid = pid_for_job(con, "application-click", appid);
681
968
        }
682
969
 
683
 
        nih_unref(proxy, NULL);
 
970
        g_object_unref(con);
684
971
 
685
972
        return pid;
686
973
}
688
975
gboolean
689
976
upstart_app_launch_pid_in_app_id (GPid pid, const gchar * appid)
690
977
{
 
978
        g_return_val_if_fail(appid != NULL, FALSE);
 
979
 
691
980
        if (pid == 0) {
692
981
                return FALSE;
693
982
        }