Skip to content

Commit d517b26

Browse files
committed
Implement SystemD activation
Closes #369 Closes #433
1 parent 65d9762 commit d517b26

6 files changed

+134
-7
lines changed

Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ PACK_CONTENT += \
192192
ddterm/com.github.amezin.ddterm.Extension.xml \
193193
ddterm/com.github.amezin.ddterm.desktop.in \
194194
ddterm/com.github.amezin.ddterm.service.in \
195+
ddterm/dbus-com.github.amezin.ddterm.service.in \
195196
LICENSE \
196197

197198
PACK_CONTENT := $(sort $(PACK_CONTENT))

ddterm/app/application.js

+45-2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
'use strict';
2121

22+
const ByteArray = imports.byteArray;
2223
const System = imports.system;
2324

2425
const Me = imports.misc.extensionUtils.getCurrentExtension();
@@ -83,6 +84,14 @@ const Application = GObject.registerClass(
8384
'Ask the extension to launch the app',
8485
null
8586
);
87+
this.add_main_option(
88+
'attach-unit',
89+
0,
90+
GLib.OptionFlags.NONE,
91+
GLib.OptionArg.STRING,
92+
'Attach launched application process to the specified systemd unit',
93+
'UNIT_NAME'
94+
);
8695

8796
this.connect('startup', this.startup.bind(this));
8897
this.connect('handle-local-options', this.handle_local_options.bind(this));
@@ -228,17 +237,30 @@ const Application = GObject.registerClass(
228237
}
229238

230239
handle_local_options(_, options) {
240+
const attach_unit = options.lookup('attach-unit');
241+
231242
if (options.lookup('launch-through-extension')) {
232243
try {
233-
const iface = extensiondbus.get();
234-
iface.ServiceSync();
244+
extensiondbus.get().ServiceSync(
245+
attach_unit ? ['--attach-unit', attach_unit] : []
246+
);
247+
235248
return 0;
236249
} catch (e) {
237250
logError(e);
238251
return 1;
239252
}
240253
}
241254

255+
if (attach_unit) {
256+
try {
257+
this.attach_unit(attach_unit);
258+
} catch (e) {
259+
logError(e);
260+
return 1;
261+
}
262+
}
263+
242264
this.decorated = !options.lookup('undecorated');
243265

244266
if (!(this.flags & Gio.ApplicationFlags.IS_SERVICE))
@@ -247,6 +269,27 @@ const Application = GObject.registerClass(
247269
return -1;
248270
}
249271

272+
attach_unit(unit) {
273+
Gio.DBus.session.call_sync(
274+
'org.freedesktop.systemd1',
275+
'/org/freedesktop/systemd1',
276+
'org.freedesktop.systemd1.Manager',
277+
'AttachProcessesToUnit',
278+
GLib.Variant.new_tuple([
279+
GLib.Variant.new_string(unit),
280+
GLib.Variant.new_string(''),
281+
GLib.Variant.new_array(
282+
new GLib.VariantType('u'),
283+
[GLib.Variant.new_uint32(Gio.Credentials.new().get_unix_pid())]
284+
),
285+
]),
286+
null,
287+
Gio.DBusCallFlags.NONE,
288+
-1,
289+
null
290+
);
291+
}
292+
250293
preferences() {
251294
if (this.prefs_dialog === null) {
252295
this.prefs_dialog = new PrefsDialog({

ddterm/com.github.amezin.ddterm.Extension.xml

+3-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
<interface name="com.github.amezin.ddterm.Extension">
66
<method name="Toggle"/>
77
<method name="Activate"/>
8-
<method name="Service"/>
8+
<method name="Service">
9+
<arg type="as" name="args" direction="in"/>
10+
</method>
911
<method name="MissingDependenciesNotification">
1012
<arg type="as" name="packages" direction="in"/>
1113
<arg type="as" name="files" direction="in"/>
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
[D-BUS Service]
22
Name=com.github.amezin.ddterm
33
Exec="@LAUNCHER@" --launch-through-extension
4+
SystemdService=dbus-com.github.amezin.ddterm.service
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[Service]
2+
Type=forking
3+
BusName=com.github.amezin.ddterm
4+
ExecStart="@LAUNCHER@" --launch-through-extension --attach-unit "%n"
5+
Delegate=

ddterm/shell/extension.js

+79-4
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ let window_connections = null;
4444
let dbus_interface = null;
4545

4646
let desktop_entry = null;
47+
let systemd_service = null;
4748
let dbus_service = null;
4849

4950
const APP_ID = 'com.github.amezin.ddterm';
@@ -127,8 +128,28 @@ class ExtensionDBusInterface {
127128
activate();
128129
}
129130

130-
Service() {
131-
spawn_app();
131+
ServiceAsync(params, invocation) {
132+
const return_error = err => {
133+
logError(err);
134+
135+
if (err instanceof GLib.Error) {
136+
invocation.return_gerror(err);
137+
} else {
138+
let name = err.name;
139+
if (!name.includes('.'))
140+
name = `org.gnome.gjs.JSError.${name}`;
141+
142+
invocation.return_dbus_error(name, err.message);
143+
}
144+
};
145+
146+
try {
147+
const [args] = params;
148+
149+
spawn_app(args).then(() => invocation.return_value(null)).catch(return_error);
150+
} catch (err) {
151+
return_error(err);
152+
}
132153
}
133154

134155
MissingDependenciesNotification(packages, files) {
@@ -292,6 +313,31 @@ function enable() {
292313
);
293314
desktop_entry.install();
294315

316+
systemd_service = new InstallableResource(
317+
Me.dir.get_child('ddterm').get_child('dbus-com.github.amezin.ddterm.service.in'),
318+
Gio.File.new_for_path(GLib.build_filenamev(
319+
[
320+
GLib.get_user_runtime_dir(),
321+
'systemd',
322+
'user',
323+
`dbus-${APP_ID}.service`,
324+
]))
325+
);
326+
systemd_service.install();
327+
328+
Gio.DBus.session.call(
329+
'org.freedesktop.systemd1',
330+
'/org/freedesktop/systemd1',
331+
'org.freedesktop.systemd1.Manager',
332+
'Reload',
333+
null,
334+
null,
335+
Gio.DBusCallFlags.NO_AUTO_START,
336+
-1,
337+
null,
338+
null
339+
);
340+
295341
dbus_service = new InstallableResource(
296342
Me.dir.get_child('ddterm').get_child('com.github.amezin.ddterm.service.in'),
297343
Gio.File.new_for_path(GLib.build_filenamev(
@@ -372,6 +418,11 @@ function disable() {
372418
desktop_entry = null;
373419
}
374420

421+
if (systemd_service) {
422+
systemd_service.uninstall();
423+
systemd_service = null;
424+
}
425+
375426
if (dbus_service) {
376427
dbus_service.uninstall();
377428
dbus_service = null;
@@ -380,7 +431,7 @@ function disable() {
380431
settings = null;
381432
}
382433

383-
function spawn_app() {
434+
async function spawn_app(args) {
384435
if (subprocess)
385436
return;
386437

@@ -393,6 +444,7 @@ function spawn_app() {
393444
Me.dir.get_child(APP_ID).get_path(),
394445
'--undecorated',
395446
'--gapplication-service',
447+
...args,
396448
];
397449

398450
if (Meta.is_wayland_compositor()) {
@@ -412,7 +464,30 @@ function spawn_app() {
412464
else
413465
subprocess = subprocess_launcher.spawnv(argv);
414466

415-
subprocess.wait_async(null, subprocess_terminated);
467+
const terminated = new Promise(resolve => {
468+
subprocess.wait_async(null, source => {
469+
subprocess_terminated(source);
470+
resolve();
471+
});
472+
});
473+
474+
let available_handler = null;
475+
476+
const registered = new Promise(resolve => {
477+
available_handler = app_dbus.connect('notify::available', source => {
478+
if (source.available)
479+
resolve();
480+
});
481+
});
482+
483+
try {
484+
await Promise.race([terminated, registered]);
485+
} finally {
486+
app_dbus.disconnect(available_handler);
487+
}
488+
489+
if (!subprocess)
490+
throw new Error('ddterm app exited without acquiring bus name');
416491
}
417492

418493
function subprocess_terminated(source) {

0 commit comments

Comments
 (0)