chiark / gitweb /
execute: talk directly to the journald, instead to the stdout-syslog-bridge
[elogind.git] / src / gnome-ask-password-agent.vala
1 /***
2   This file is part of systemd.
3
4   Copyright 2010 Lennart Poettering
5
6   systemd is free software; you can redistribute it and/or modify it
7   under the terms of the GNU General Public License as published by
8   the Free Software Foundation; either version 2 of the License, or
9   (at your option) any later version.
10
11   systemd is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14   General Public License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 using Gtk;
21 using GLib;
22 using Linux;
23 using Posix;
24 using Notify;
25
26 [CCode (cheader_filename = "time.h")]
27 extern int clock_gettime(int id, out timespec ts);
28
29 public class PasswordDialog : Dialog {
30
31         public Entry entry;
32
33         public PasswordDialog(string message, string icon) {
34                 set_title("System Password");
35                 set_has_separator(false);
36                 set_border_width(8);
37                 set_default_response(ResponseType.OK);
38                 set_icon_name(icon);
39
40                 add_button(Stock.CANCEL, ResponseType.CANCEL);
41                 add_button(Stock.OK, ResponseType.OK);
42
43                 Container content = (Container) get_content_area();
44
45                 Box hbox = new HBox(false, 16);
46                 hbox.set_border_width(8);
47                 content.add(hbox);
48
49                 Image image = new Image.from_icon_name(icon, IconSize.DIALOG);
50                 hbox.pack_start(image, false, false);
51
52                 Box vbox = new VBox(false, 8);
53                 hbox.pack_start(vbox, true, true);
54
55                 Label label = new Label(message);
56                 vbox.pack_start(label, false, false);
57
58                 entry = new Entry();
59                 entry.set_visibility(false);
60                 entry.set_activates_default(true);
61                 vbox.pack_start(entry, false, false);
62
63                 entry.activate.connect(on_entry_activated);
64
65                 show_all();
66         }
67
68         public void on_entry_activated() {
69                 response(ResponseType.OK);
70         }
71 }
72
73 public class MyStatusIcon : StatusIcon {
74
75         File directory;
76         File current;
77         FileMonitor file_monitor;
78
79         string message;
80         string icon;
81         string socket;
82
83         PasswordDialog password_dialog;
84
85         public MyStatusIcon() throws GLib.Error {
86                 GLib.Object(icon_name : "dialog-password");
87                 set_title("System Password Request");
88
89                 directory = File.new_for_path("/run/systemd/ask-password/");
90                 file_monitor = directory.monitor_directory(0);
91                 file_monitor.changed.connect(file_monitor_changed);
92
93                 current = null;
94                 look_for_password();
95
96                 activate.connect(status_icon_activate);
97         }
98
99         void file_monitor_changed(GLib.File file, GLib.File? other_file, GLib.FileMonitorEvent event_type) {
100
101                 if (!file.get_basename().has_prefix("ask."))
102                         return;
103
104                 if (event_type == FileMonitorEvent.CREATED ||
105                     event_type == FileMonitorEvent.DELETED) {
106                         try {
107                                 look_for_password();
108                         } catch (Error e) {
109                                 show_error(e.message);
110                         }
111                 }
112         }
113
114         void look_for_password() throws GLib.Error {
115
116                 if (current != null) {
117                         if (!current.query_exists()) {
118                                 current = null;
119                                 if (password_dialog != null)
120                                         password_dialog.response(ResponseType.REJECT);
121                         }
122                 }
123
124                 if (current == null) {
125                         FileEnumerator enumerator = directory.enumerate_children("standard::name", FileQueryInfoFlags.NOFOLLOW_SYMLINKS);
126
127                         FileInfo i;
128                         while ((i = enumerator.next_file()) != null) {
129                                 if (!i.get_name().has_prefix("ask."))
130                                         continue;
131
132                                 current = directory.get_child(i.get_name());
133
134                                 if (load_password())
135                                         break;
136
137                                 current = null;
138                         }
139                 }
140
141                 if (current == null)
142                         set_visible(false);
143         }
144
145         bool load_password() throws GLib.Error {
146
147                 KeyFile key_file = new KeyFile();
148
149                 try {
150                         timespec ts;
151
152                         key_file.load_from_file(current.get_path(), KeyFileFlags.NONE);
153
154                         string not_after_as_string = key_file.get_string("Ask", "NotAfter");
155
156                         clock_gettime(1, out ts);
157                         uint64 now = (ts.tv_sec * 1000000) + (ts.tv_nsec / 1000);
158
159                         uint64 not_after;
160                         if (not_after_as_string.scanf("%llu", out not_after) != 1)
161                                 return false;
162
163                         if (not_after > 0 && not_after < now)
164                                 return false;
165
166                         socket = key_file.get_string("Ask", "Socket");
167                 } catch (GLib.Error e) {
168                         return false;
169                 }
170
171                 try {
172                         message = key_file.get_string("Ask", "Message").compress();
173                 } catch (GLib.Error e) {
174                         message = "Please Enter System Password!";
175                 }
176
177                 set_tooltip_text(message);
178
179                 try {
180                         icon = key_file.get_string("Ask", "Icon");
181                 } catch (GLib.Error e) {
182                         icon = "dialog-password";
183                 }
184                 set_from_icon_name(icon);
185
186                 set_visible(true);
187
188                 Notification n = new Notification(title, message, icon);
189                 n.set_timeout(5000);
190                 n.show();
191
192                 return true;
193         }
194
195         void status_icon_activate() {
196
197                 if (current == null)
198                         return;
199
200                 if (password_dialog != null) {
201                         password_dialog.present();
202                         return;
203                 }
204
205                 password_dialog = new PasswordDialog(message, icon);
206
207                 int result = password_dialog.run();
208                 string password = password_dialog.entry.get_text();
209
210                 password_dialog.destroy();
211                 password_dialog = null;
212
213                 if (result == ResponseType.REJECT ||
214                     result == ResponseType.DELETE_EVENT)
215                         return;
216
217                 int to_process;
218
219                 try {
220                         Process.spawn_async_with_pipes(
221                                         null,
222                                         { "/usr/bin/pkexec", "/lib/systemd/systemd-reply-password", result == ResponseType.OK ? "1" : "0", socket },
223                                         null,
224                                         0,
225                                         null,
226                                         null,
227                                         out to_process,
228                                         null,
229                                         null);
230
231                         OutputStream stream = new UnixOutputStream(to_process, true);
232 #if VALA_0_12
233                         stream.write(password.data, null);
234 #else
235                         stream.write(password, password.length, null);
236 #endif
237                 } catch (Error e) {
238                         show_error(e.message);
239                 }
240         }
241 }
242
243 static const OptionEntry entries[] = {
244         { null }
245 };
246
247 void show_error(string e) {
248         var m = new MessageDialog(null, 0, MessageType.ERROR, ButtonsType.CLOSE, "%s", e);
249         m.run();
250         m.destroy();
251 }
252
253 int main(string[] args) {
254         try {
255                 Gtk.init_with_args(ref args, "[OPTION...]", entries, "systemd-ask-password-agent");
256                 Notify.init("Password Agent");
257
258                 MyStatusIcon i = new MyStatusIcon();
259                 Gtk.main();
260
261         } catch (GLib.Error e) {
262                 show_error(e.message);
263         }
264
265         return 0;
266 }