1 /* gbp-deviced-runner.c
2 *
3 * Copyright 2021 James Westman <james@jwestman.net>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: GPL-3.0-or-later
19 */
20
21 #define G_LOG_DOMAIN "gbp-deviced-runner"
22
23 #include "../flatpak/gbp-flatpak-manifest.h"
24
25 #include "gbp-deviced-runner.h"
26
27 struct _GbpDevicedRunner
28 {
29 IdeRunner parent_instance;
30
31 GbpDevicedDevice *device;
32 };
33
34 G_DEFINE_TYPE (GbpDevicedRunner, gbp_deviced_runner, IDE_TYPE_RUNNER)
35
36 enum {
37 PROP_0,
38 PROP_DEVICE,
39 N_PROPS
40 };
41
42 static GParamSpec *properties [N_PROPS];
43
44 GbpDevicedRunner *
gbp_deviced_runner_new(GbpDevicedDevice * device)45 gbp_deviced_runner_new (GbpDevicedDevice *device)
46 {
47 return g_object_new (GBP_TYPE_DEVICED_RUNNER,
48 "device", device,
49 NULL);
50 }
51
52 static void
gbp_deviced_runner_finalize(GObject * object)53 gbp_deviced_runner_finalize (GObject *object)
54 {
55 GbpDevicedRunner *self = (GbpDevicedRunner *)object;
56
57 g_clear_object (&self->device);
58
59 G_OBJECT_CLASS (gbp_deviced_runner_parent_class)->finalize (object);
60 }
61
62 static void
gbp_deviced_runner_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)63 gbp_deviced_runner_get_property (GObject *object,
64 guint prop_id,
65 GValue *value,
66 GParamSpec *pspec)
67 {
68 GbpDevicedRunner *self = GBP_DEVICED_RUNNER (object);
69
70 switch (prop_id)
71 {
72 case PROP_DEVICE:
73 g_value_set_object (value, gbp_deviced_runner_get_device (self));
74 break;
75 default:
76 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
77 }
78 }
79
80 static void
gbp_deviced_runner_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)81 gbp_deviced_runner_set_property (GObject *object,
82 guint prop_id,
83 const GValue *value,
84 GParamSpec *pspec)
85 {
86 GbpDevicedRunner *self = GBP_DEVICED_RUNNER (object);
87
88 switch (prop_id)
89 {
90 case PROP_DEVICE:
91 gbp_deviced_runner_set_device (self, g_value_get_object (value));
92 break;
93 default:
94 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
95 }
96 }
97
98 typedef struct {
99 GbpDevicedRunner *self;
100 DevdClient *client;
101 char *app_id;
102 DevdProcessService *process_service;
103 char *process_id;
104 char *pty_id;
105 int tty_fd;
106 GCancellable *cancellable;
107 gulong cancellable_handle;
108 } RunData;
109
110 static void
run_data_free(RunData * data)111 run_data_free (RunData *data)
112 {
113 g_clear_object (&data->self);
114 g_clear_object (&data->client);
115 g_clear_object (&data->process_service);
116 g_clear_pointer (&data->app_id, g_free);
117 g_clear_pointer (&data->process_id, g_free);
118 g_clear_pointer (&data->pty_id, g_free);
119 g_cancellable_disconnect (data->cancellable, data->cancellable_handle);
120 g_free (data);
121 }
122
123 static void run_wait_for_process_loop (IdeTask *task);
124
125 static void
run_wait_for_process_cb(GObject * object,GAsyncResult * result,gpointer user_data)126 run_wait_for_process_cb (GObject *object,
127 GAsyncResult *result,
128 gpointer user_data)
129 {
130 DevdProcessService *process_service = (DevdProcessService *)object;
131 g_autoptr(IdeTask) task = user_data;
132 g_autoptr(GError) error = NULL;
133 RunData *data;
134 gboolean exited;
135 int exit_code;
136 int term_sig;
137 GCancellable *cancellable;
138
139 g_assert (DEVD_IS_PROCESS_SERVICE (process_service));
140 g_assert (IDE_IS_TASK (task));
141
142 data = ide_task_get_task_data (task);
143 cancellable = ide_task_get_cancellable (task);
144
145 devd_process_service_wait_for_process_finish (process_service,
146 result,
147 &exited,
148 &exit_code,
149 &term_sig,
150 &error);
151 if (error != NULL)
152 {
153 ide_task_return_error (task, g_steal_pointer (&error));
154 return;
155 }
156
157 if (exited)
158 {
159 if (data->pty_id != NULL)
160 devd_process_service_destroy_pty_async (process_service,
161 data->pty_id,
162 cancellable,
163 NULL, NULL);
164
165 if (data->tty_fd != -1)
166 close(data->tty_fd);
167
168 ide_task_return_boolean (task, TRUE);
169 }
170 else
171 {
172 run_wait_for_process_loop (task);
173 }
174 }
175
176 static void
run_wait_for_process_loop(IdeTask * task)177 run_wait_for_process_loop (IdeTask *task)
178 {
179 GCancellable *cancellable;
180 RunData *data;
181
182 data = ide_task_get_task_data (task);
183 cancellable = ide_task_get_cancellable (task);
184
185 devd_process_service_wait_for_process_async (data->process_service,
186 data->process_id,
187 cancellable,
188 run_wait_for_process_cb,
189 g_object_ref (task));
190 }
191
192 static void
run_cancelled_cb(GCancellable * cancellable,RunData * data)193 run_cancelled_cb (GCancellable *cancellable,
194 RunData *data)
195 {
196 IDE_ENTRY;
197
198 g_assert (G_IS_CANCELLABLE (cancellable));
199
200 if (data->process_service)
201 devd_process_service_force_exit (data->process_service, data->process_id);
202
203 IDE_EXIT;
204 }
205
206 static void
run_run_app_cb(GObject * object,GAsyncResult * result,gpointer user_data)207 run_run_app_cb (GObject *object,
208 GAsyncResult *result,
209 gpointer user_data)
210 {
211 DevdClient *client = (DevdClient *)object;
212 g_autoptr(IdeTask) task = user_data;
213 g_autoptr(GError) error = NULL;
214 RunData *data;
215
216 g_assert (DEVD_IS_CLIENT (client));
217 g_assert (IDE_IS_TASK (task));
218
219 data = ide_task_get_task_data (task);
220
221 if (!(data->process_id = devd_client_run_app_finish (client, result, &error)))
222 {
223 ide_task_return_error (task, g_steal_pointer (&error));
224 return;
225 }
226
227 data->cancellable = ide_task_get_cancellable (task);
228 g_cancellable_connect (data->cancellable, G_CALLBACK (run_cancelled_cb), data, NULL);
229
230 run_wait_for_process_loop (task);
231 }
232
233 static void
run_create_pty_cb(GObject * object,GAsyncResult * result,gpointer user_data)234 run_create_pty_cb (GObject *object,
235 GAsyncResult *result,
236 gpointer user_data)
237 {
238 RunData *data;
239 DevdProcessService *process_service = (DevdProcessService *)object;
240 g_autoptr(IdeTask) task = user_data;
241 g_autoptr(GError) error = NULL;
242
243 IDE_ENTRY;
244
245 g_assert (DEVD_IS_PROCESS_SERVICE (process_service));
246 g_assert (IDE_IS_TASK (task));
247
248 data = ide_task_get_task_data (task);
249
250 if (!(data->pty_id = devd_process_service_create_pty_finish (process_service, result, &error)))
251 {
252 ide_task_return_error (task, g_steal_pointer (&error));
253 IDE_EXIT;
254 }
255
256 devd_client_run_app_async (data->client,
257 "flatpak",
258 data->app_id,
259 data->pty_id,
260 ide_task_get_cancellable (task),
261 run_run_app_cb,
262 g_object_ref (task));
263
264 IDE_EXIT;
265 }
266
267 static void
run_get_client_cb(GObject * object,GAsyncResult * result,gpointer user_data)268 run_get_client_cb (GObject *object,
269 GAsyncResult *result,
270 gpointer user_data)
271 {
272 RunData *data;
273 GbpDevicedDevice *device = (GbpDevicedDevice *)object;
274 g_autoptr(IdeTask) task = user_data;
275 g_autoptr(GError) error = NULL;
276 VtePty *pty;
277
278 IDE_ENTRY;
279
280 g_assert (GBP_IS_DEVICED_DEVICE (device));
281 g_assert (IDE_IS_TASK (task));
282
283 data = ide_task_get_task_data (task);
284
285 if (!(data->client = gbp_deviced_device_get_client_finish (device, result, &error)))
286 {
287 ide_task_return_error (task, g_steal_pointer (&error));
288 return;
289 }
290
291 pty = ide_runner_get_pty (IDE_RUNNER (data->self));
292 if (pty == NULL || ide_runner_get_disable_pty (IDE_RUNNER (data->self)))
293 {
294 devd_client_run_app_async (data->client,
295 "flatpak",
296 data->app_id,
297 NULL,
298 ide_task_get_cancellable (task),
299 run_run_app_cb,
300 g_object_ref (task));
301 IDE_EXIT;
302 }
303
304 data->process_service = devd_process_service_new (data->client, &error);
305 if (error != NULL)
306 {
307 ide_task_return_error (task, g_steal_pointer (&error));
308 IDE_EXIT;
309 }
310
311 data->tty_fd = ide_pty_intercept_create_slave (vte_pty_get_fd (pty), TRUE);
312 devd_process_service_create_pty_async (data->process_service,
313 data->tty_fd,
314 ide_task_get_cancellable (task),
315 run_create_pty_cb,
316 g_object_ref (task));
317
318 IDE_EXIT;
319 }
320
321 static void
gbp_deviced_runner_run_async(IdeRunner * runner,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)322 gbp_deviced_runner_run_async (IdeRunner *runner,
323 GCancellable *cancellable,
324 GAsyncReadyCallback callback,
325 gpointer user_data)
326 {
327 GbpDevicedRunner *self = (GbpDevicedRunner *)runner;
328 g_autoptr(IdeTask) task = NULL;
329 IdeContext *context;
330 IdeConfigManager *config_manager;
331 IdeConfig *config;
332 RunData *data;
333
334 IDE_ENTRY;
335
336 g_return_if_fail (GBP_IS_DEVICED_RUNNER (self));
337 g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
338
339 task = ide_task_new (self, cancellable, callback, user_data);
340 ide_task_set_source_tag (task, gbp_deviced_runner_run_async);
341
342 if (self->device == NULL)
343 {
344 ide_task_return_new_error (task,
345 G_IO_ERROR,
346 G_IO_ERROR_FAILED,
347 "No device set on deviced runner");
348 IDE_EXIT;
349 }
350
351 context = ide_object_get_context (IDE_OBJECT (self->device));
352 g_assert (IDE_IS_CONTEXT (context));
353 config_manager = ide_config_manager_from_context (context);
354 config = ide_config_manager_get_current (config_manager);
355
356 data = g_new0 (RunData, 1);
357
358 /* GbpDevicedDeployStrategy only supports flatpak manifests, and it is the
359 * only thing that creates GbpDevicedRunners, so we should only get flatpak
360 * manifests here */
361 g_assert (GBP_IS_FLATPAK_MANIFEST (config));
362
363 ide_task_set_task_data (task, data, run_data_free);
364
365 data->self = g_object_ref (self);
366 data->app_id = g_strdup (ide_config_get_app_id (config));
367 data->tty_fd = -1;
368
369 gbp_deviced_device_get_client_async (self->device,
370 cancellable,
371 run_get_client_cb,
372 g_object_ref (task));
373
374 IDE_EXIT;
375 }
376
377 static gboolean
gbp_deviced_runner_run_finish(IdeRunner * self,GAsyncResult * result,GError ** error)378 gbp_deviced_runner_run_finish (IdeRunner *self,
379 GAsyncResult *result,
380 GError **error)
381 {
382 gboolean ret;
383
384 IDE_ENTRY;
385
386 g_return_val_if_fail (GBP_IS_DEVICED_RUNNER (self), FALSE);
387 g_return_val_if_fail (ide_task_is_valid (result, self), FALSE);
388
389 ret = ide_task_propagate_boolean (IDE_TASK (result), error);
390
391 IDE_RETURN (ret);
392 }
393
394 static void
gbp_deviced_runner_class_init(GbpDevicedRunnerClass * klass)395 gbp_deviced_runner_class_init (GbpDevicedRunnerClass *klass)
396 {
397 GObjectClass *object_class = G_OBJECT_CLASS (klass);
398 IdeRunnerClass *runner_class = IDE_RUNNER_CLASS (klass);
399
400 object_class->finalize = gbp_deviced_runner_finalize;
401 object_class->get_property = gbp_deviced_runner_get_property;
402 object_class->set_property = gbp_deviced_runner_set_property;
403
404 runner_class->run_async = gbp_deviced_runner_run_async;
405 runner_class->run_finish = gbp_deviced_runner_run_finish;
406
407 properties[PROP_DEVICE] =
408 g_param_spec_object ("device",
409 "Device",
410 "The device to run on",
411 GBP_TYPE_DEVICED_DEVICE,
412 G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
413
414 g_object_class_install_properties (G_OBJECT_CLASS (klass), N_PROPS, properties);
415 }
416
417 static void
gbp_deviced_runner_init(GbpDevicedRunner * self)418 gbp_deviced_runner_init (GbpDevicedRunner *self)
419 {
420 }
421
422 GbpDevicedDevice *
gbp_deviced_runner_get_device(GbpDevicedRunner * self)423 gbp_deviced_runner_get_device (GbpDevicedRunner *self)
424 {
425 g_return_val_if_fail (GBP_IS_DEVICED_RUNNER (self), NULL);
426 return self->device;
427 }
428
429 void
gbp_deviced_runner_set_device(GbpDevicedRunner * self,GbpDevicedDevice * device)430 gbp_deviced_runner_set_device (GbpDevicedRunner *self,
431 GbpDevicedDevice *device)
432 {
433 g_return_if_fail (GBP_IS_DEVICED_RUNNER (self));
434 g_set_object (&self->device, device);
435 }
436