diff --git a/meson.build b/meson.build
index b53590c3..2b08ec2a 100644
--- a/meson.build
+++ b/meson.build
@@ -143,7 +143,6 @@ glib = dependency('glib-2.0', version: '> 2.26')
gio_unix = dependency('gio-unix-2.0')
gee = dependency('gee-0.8')
gtk3 = dependency('gtk+-3.0')
-libnotify = dependency('libnotify')
posix = meson.get_compiler('vala').find_library('posix')
#####################################################################
diff --git a/src/gnome-ask-password-agent.vala b/src/gnome-ask-password-agent.vala
index 7124d3f8..f77a14d8 100644
--- a/src/gnome-ask-password-agent.vala
+++ b/src/gnome-ask-password-agent.vala
@@ -2,6 +2,7 @@
This file is part of systemd.
Copyright 2010 Lennart Poettering
+ Copyright 2024 Ben Boeckel
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
@@ -17,10 +18,10 @@
along with systemd; If not, see .
***/
+using Gee;
using Gtk;
using GLib;
using Posix;
-using Notify;
[CCode (cheader_filename = "time.h")]
extern int clock_gettime(int id, out timespec ts);
@@ -29,8 +30,8 @@ public class PasswordDialog : Dialog {
public Entry entry;
- public PasswordDialog(string message, string icon) {
- set_title("System Password");
+ public PasswordDialog(string domain, string message, string icon) {
+ set_title("%s Password".printf(domain));
set_border_width(8);
set_default_response(ResponseType.OK);
set_icon_name(icon);
@@ -68,87 +69,69 @@ public class PasswordDialog : Dialog {
}
}
-public class MyStatusIcon : StatusIcon {
-
+class Watch : GLib.Object {
File directory;
- File current;
FileMonitor file_monitor;
- string message;
- string icon;
- string socket;
+ private weak Application app;
- PasswordDialog password_dialog;
- Notify.Notification n;
+ string title;
+ string domain_display;
+ string domain;
- public MyStatusIcon() throws GLib.Error {
- GLib.Object(icon_name : "dialog-password");
- set_title("System Password Request");
+ public Watch(Application gapp, string domain, string path) throws GLib.Error {
+ app = gapp;
- directory = File.new_for_path("/run/systemd/ask-password/");
+ directory = File.new_for_path(path);
file_monitor = directory.monitor_directory(0);
file_monitor.changed.connect(file_monitor_changed);
- current = null;
- look_for_password();
+ domain_display = "%s%s".printf(domain.ascii_up(1), domain.substring(1));
+ title = "Password Request (%s)".printf(domain_display);
+ this.domain = domain;
- activate.connect(status_icon_activate);
+ look_in_directory(directory);
}
- void file_monitor_changed(GLib.File file, GLib.File? other_file, GLib.FileMonitorEvent event_type) {
+ void look_in_directory(File dir) throws GLib.Error {
+ FileEnumerator enumerator = dir.enumerate_children("standard::name", FileQueryInfoFlags.NOFOLLOW_SYMLINKS);
+
+ FileInfo i;
+ while ((i = enumerator.next_file()) != null) {
+ if (!i.get_name().has_prefix("ask.")) {
+ continue;
+ }
+
+ load_password(dir.get_child(i.get_name()));
+ }
+ }
- if (!file.get_basename().has_prefix("ask."))
+ void file_monitor_changed(GLib.File file, GLib.File? other_file, GLib.FileMonitorEvent event_type) {
+ if (!file.get_basename().has_prefix("ask.")) {
return;
+ }
if (event_type == FileMonitorEvent.CREATED ||
event_type == FileMonitorEvent.DELETED) {
try {
- look_for_password();
+ load_password(file);
} catch (Error e) {
show_error(e.message);
}
}
}
- void look_for_password() throws GLib.Error {
-
- if (current != null) {
- if (!current.query_exists()) {
- current = null;
- if (password_dialog != null)
- password_dialog.response(ResponseType.REJECT);
- }
- }
-
- if (current == null) {
- FileEnumerator enumerator = directory.enumerate_children("standard::name", FileQueryInfoFlags.NOFOLLOW_SYMLINKS);
-
- FileInfo i;
- while ((i = enumerator.next_file()) != null) {
- if (!i.get_name().has_prefix("ask."))
- continue;
-
- current = directory.get_child(i.get_name());
-
- if (load_password())
- break;
-
- current = null;
- }
- }
-
- if (current == null)
- set_visible(false);
- }
-
- bool load_password() throws GLib.Error {
-
+ bool load_password(File file) throws GLib.Error {
KeyFile key_file = new KeyFile();
+ int? timeout = null;
+ string socket;
+ string message;
+ string icon;
try {
timespec ts;
- key_file.load_from_file(current.get_path(), KeyFileFlags.NONE);
+ key_file.load_from_file(file.get_path(), KeyFileFlags.NONE);
string not_after_as_string = key_file.get_string("Ask", "NotAfter");
@@ -157,11 +140,17 @@ public class MyStatusIcon : StatusIcon {
uint64 not_after = uint64.parse(not_after_as_string);;
if ((not_after == 0 && GLib.errno == Posix.EINVAL) ||
- (not_after == int64.MAX && GLib.errno == Posix.ERANGE))
+ (not_after == int64.MAX && GLib.errno == Posix.ERANGE)) {
return false;
+ }
- if (not_after > 0 && not_after < now)
+ if (not_after > 0 && not_after < now) {
return false;
+ }
+
+ if (not_after > 0) {
+ timeout = (int)(not_after - now) / 1000;
+ }
socket = key_file.get_string("Ask", "Socket");
} catch (GLib.Error e) {
@@ -171,51 +160,142 @@ public class MyStatusIcon : StatusIcon {
try {
message = key_file.get_string("Ask", "Message").compress();
} catch (GLib.Error e) {
- message = "Please Enter System Password!";
+ message = "Please Enter %s Password!".printf(domain_display);
}
- set_tooltip_text(message);
-
try {
icon = key_file.get_string("Ask", "Icon");
} catch (GLib.Error e) {
icon = "dialog-password";
}
- set_from_icon_name(icon);
- n = new Notify.Notification(title, message, icon);
- n.set_timeout(5000);
- n.closed.connect(() => {
- set_visible(true);
+ GLib.Notification n = new GLib.Notification(title);
+ n.set_category("password.request." + domain);
+ n.set_body(message);
+ n.set_priority(GLib.NotificationPriority.NORMAL);
+ n.set_icon(new ThemedIcon(icon));
+ n.add_button_with_target("Enter password", "app.password-request", "(ssss)", domain, message, icon, socket);
+
+ string n_id = "password-request-%s".printf(socket);
+ app.send_notification(n_id, n);
+ if (timeout != null) {
+ uint s = GLib.Timeout.add_once((!) timeout, () => {
+ app.withdraw_notification(n_id);
+ app.timeouts.unset(socket);
});
- n.add_action("enter_pw", "Enter password", status_icon_activate);
- n.show();
+ app.timeouts[socket] = s;
+ }
return true;
}
+}
+
+void show_error(string e) {
+ Posix.stderr.printf("%s\n", e);
+ var m = new MessageDialog(null, 0, MessageType.ERROR, ButtonsType.CLOSE, "%s", e);
+ m.run();
+ m.destroy();
+}
+
+class Application : Gtk.Application {
+ private static bool system = false;
+ private static bool user = false;
+
+ private Watch? system_watch = null;
+ private Watch? user_watch = null;
+
+ public Gee.HashMap timeouts;
+
+ private const OptionEntry entries[] = {
+ { "system", 's', OptionFlags.NONE, OptionArg.NONE, ref system, "Watch for system requests", null },
+ { "user", 'u', OptionFlags.NONE, OptionArg.NONE, ref user, "Watch for system requests", null },
+ { null }
+ };
+
+ private const GLib.ActionEntry actions[] = {
+ { "password-request", password_request, "(ssss)" },
+ };
+
+ public Application() {
+ Object(application_id: "org.freedesktop.systemd.gnome-ask-password-agent",
+ flags: GLib.ApplicationFlags.IS_SERVICE);
+ add_main_option_entries(entries);
+ add_action_entries(actions, this);
+ set_default(this);
+
+ timeouts = new Gee.HashMap();
+ }
+
+ protected override void startup() {
+ if (system) {
+ system_watch = add_watch("system", "/run/systemd/ask-password/");
+ }
+
+ if (user) {
+ string? xdg_runtime_dir = Environment.get_variable("XDG_RUNTIME_DIR");
+ if (xdg_runtime_dir == null) {
+ show_error("no user XDG runtime directory");
+ } else {
+ add_watch("user", (!) xdg_runtime_dir + "/systemd/ask-password/");
+ }
+ }
+
+ if (system_watch != null || user_watch != null) {
+ hold();
+ } else {
+ show_error("no watches requested");
+ }
+ }
+
+ private Watch? add_watch(string domain, string path) {
+ try {
+ return new Watch(this, domain, path);
+ } catch (IOError e) {
+ show_error("failed to set up %s watches on %s: %s".printf(domain, path, e.message));
+ } catch (GLib.Error e) {
+ show_error("failed to set up %s watches on %s: %s".printf(domain, path, e.message));
+ }
+
+ return null;
+ }
- void status_icon_activate() {
+ private static void password_request(GLib.SimpleAction action, GLib.Variant? variant) {
+ var gapp = GLib.Application.get_default();
+ if (gapp == null) {
+ return;
+ }
+ var app = (Application) (!) gapp;
- if (current == null)
+ if (variant.n_children() != 4) {
return;
+ }
+
+ string domain = variant.get_child_value(0).get_string();
+ string message = variant.get_child_value(1).get_string();
+ string icon = variant.get_child_value(2).get_string();
+ string socket = variant.get_child_value(3).get_string();
- if (password_dialog != null) {
- password_dialog.present();
+ if (domain.length == 0 || message.length == 0 || icon.length == 0 || socket.length == 0) {
+ show_error("invalid password request (domain: '%s', message: '%s', icon: '%s', socket: '%s')".printf(domain, message, icon, socket));
return;
}
- password_dialog = new PasswordDialog(message, icon);
+ PasswordDialog password_dialog = new PasswordDialog(domain, message, icon);
int result = password_dialog.run();
string password = password_dialog.entry.get_text();
-
password_dialog.destroy();
- password_dialog = null;
+
+ uint n_id;
+ if (app.timeouts.unset(socket, out n_id)) {
+ GLib.Source.remove(n_id);
+ }
if (result == ResponseType.REJECT ||
result == ResponseType.DELETE_EVENT ||
- result == ResponseType.CANCEL)
+ result == ResponseType.CANCEL) {
return;
+ }
Pid child_pid;
int to_process;
@@ -241,31 +321,15 @@ public class MyStatusIcon : StatusIcon {
show_error(e.message);
}
}
-}
-
-const OptionEntry entries[] = {
- { null }
-};
-
-void show_error(string e) {
- Posix.stderr.printf("%s\n", e);
- var m = new MessageDialog(null, 0, MessageType.ERROR, ButtonsType.CLOSE, "%s", e);
- m.run();
- m.destroy();
-}
-int main(string[] args) {
- try {
- Gtk.init_with_args(ref args, "[OPTION...]", entries, "systemd-ask-password-agent");
- Notify.init("Password Agent");
-
- MyStatusIcon i = new MyStatusIcon();
- Gtk.main();
- } catch (IOError e) {
- show_error(e.message);
- } catch (GLib.Error e) {
- Posix.stderr.printf("%s\n", e.message);
+ public static int main(string[] args) {
+ try {
+ Gtk.init_with_args(ref args, "[OPTION...]", entries, "systemd-ask-password-agent");
+ } catch (GLib.Error e) {
+ Posix.stderr.printf("%s\n", e.message);
+ return 1;
+ }
+ Application app = new Application();
+ return app.run(args);
}
-
- return 0;
}
diff --git a/src/meson.build b/src/meson.build
index 449a4770..c8039614 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -8,8 +8,9 @@ install_data('systemadm.appdata.xml', install_dir: appdatadir)
sgapa_files = files('gnome-ask-password-agent.vala')
sgapa = executable('systemd-gnome-ask-password-agent', sgapa_files,
- dependencies: [common_flags, gtk3, gee, gio_unix, libnotify, posix],
+ dependencies: [common_flags, gtk3, gee, gio_unix, posix],
install: true)
+install_data('org.freedesktop.systemd.gnome-ask-password-agent.desktop', install_dir: applicationsdir)
install_data('systemd-gnome-ask-password-agent.rules', install_dir: polkitrulesdir)
sgapa_units = files('systemd-gnome-ask-password-agent.path',
'systemd-gnome-ask-password-agent.service')
diff --git a/src/org.freedesktop.systemd.gnome-ask-password-agent.desktop b/src/org.freedesktop.systemd.gnome-ask-password-agent.desktop
new file mode 100644
index 00000000..fbcab9df
--- /dev/null
+++ b/src/org.freedesktop.systemd.gnome-ask-password-agent.desktop
@@ -0,0 +1,11 @@
+[Desktop Entry]
+Name=systemd GNOME ask password agent
+Comment=Agent for system- and user-level password requests
+Exec=systemd-gnome-ask-password-agent
+Icon=org.freedesktop.systemd.gnome-ask-password-agent
+Terminal=false
+Type=Application
+Categories=Utility
+StartupNotify=true
+DBusActivatable=true
+X-GNOME-UsesNotifications=true
diff --git a/src/systemd-gnome-ask-password-agent.service b/src/systemd-gnome-ask-password-agent.service
index 983d0d4d..e8b627e9 100644
--- a/src/systemd-gnome-ask-password-agent.service
+++ b/src/systemd-gnome-ask-password-agent.service
@@ -12,4 +12,4 @@ Description=Forward Password Requests to GNOME Password Agent
After=systemd-user-sessions.service
[Service]
-ExecStart=systemd-gnome-ask-password-agent
+ExecStart=systemd-gnome-ask-password-agent --system --user