]>
Commit | Line | Data |
---|---|---|
490aed58 LP |
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 DBus; | |
23 | using Linux; | |
24 | using Posix; | |
efb3237e | 25 | using Notify; |
490aed58 LP |
26 | |
27 | [CCode (cheader_filename = "time.h")] | |
28 | extern int clock_gettime(int id, out timespec ts); | |
29 | ||
30 | public class PasswordDialog : Dialog { | |
31 | ||
32 | public Entry entry; | |
33 | ||
34 | public PasswordDialog(string message, string icon) { | |
35 | set_title("System Password"); | |
36 | set_has_separator(false); | |
37 | set_border_width(8); | |
38 | set_default_response(ResponseType.OK); | |
39 | set_icon_name(icon); | |
40 | ||
230e5a3f KS |
41 | add_button(Stock.CANCEL, ResponseType.CANCEL); |
42 | add_button(Stock.OK, ResponseType.OK); | |
490aed58 LP |
43 | |
44 | Container content = (Container) get_content_area(); | |
45 | ||
46 | Box hbox = new HBox(false, 16); | |
47 | hbox.set_border_width(8); | |
48 | content.add(hbox); | |
49 | ||
50 | Image image = new Image.from_icon_name(icon, IconSize.DIALOG); | |
51 | hbox.pack_start(image, false, false); | |
52 | ||
53 | Box vbox = new VBox(false, 8); | |
54 | hbox.pack_start(vbox, true, true); | |
55 | ||
56 | Label label = new Label(message); | |
57 | vbox.pack_start(label, false, false); | |
58 | ||
59 | entry = new Entry(); | |
60 | entry.set_visibility(false); | |
61 | entry.set_activates_default(true); | |
62 | vbox.pack_start(entry, false, false); | |
63 | ||
64 | entry.activate.connect(on_entry_activated); | |
65 | ||
66 | show_all(); | |
67 | } | |
68 | ||
69 | public void on_entry_activated() { | |
70 | response(ResponseType.OK); | |
71 | } | |
72 | } | |
73 | ||
74 | public class MyStatusIcon : StatusIcon { | |
75 | ||
76 | File directory; | |
77 | File current; | |
78 | FileMonitor file_monitor; | |
79 | ||
80 | string message; | |
81 | string icon; | |
82 | string socket; | |
83 | ||
84 | PasswordDialog password_dialog; | |
85 | ||
86 | public MyStatusIcon() throws GLib.Error { | |
87 | GLib.Object(icon_name : "dialog-password"); | |
efb3237e | 88 | set_title("System Password"); |
490aed58 LP |
89 | |
90 | directory = File.new_for_path("/dev/.systemd/ask-password/"); | |
91 | file_monitor = directory.monitor_directory(0); | |
92 | file_monitor.changed.connect(file_monitor_changed); | |
93 | ||
94 | current = null; | |
95 | look_for_password(); | |
96 | ||
97 | activate.connect(status_icon_activate); | |
98 | } | |
99 | ||
100 | void file_monitor_changed(GLib.File file, GLib.File? other_file, GLib.FileMonitorEvent event_type) throws GLib.Error { | |
101 | ||
102 | if (!file.get_basename().has_prefix("ask.")) | |
103 | return; | |
104 | ||
105 | if (event_type == FileMonitorEvent.CREATED || | |
106 | event_type == FileMonitorEvent.DELETED) | |
107 | look_for_password(); | |
108 | } | |
109 | ||
110 | void look_for_password() throws GLib.Error { | |
111 | ||
112 | if (current != null) { | |
113 | if (!current.query_exists()) { | |
114 | current = null; | |
115 | if (password_dialog != null) | |
116 | password_dialog.response(ResponseType.REJECT); | |
117 | } | |
118 | } | |
119 | ||
120 | if (current == null) { | |
121 | FileEnumerator enumerator = directory.enumerate_children("standard::name", FileQueryInfoFlags.NOFOLLOW_SYMLINKS); | |
122 | ||
123 | FileInfo i; | |
124 | while ((i = enumerator.next_file()) != null) { | |
125 | if (!i.get_name().has_prefix("ask.")) | |
126 | continue; | |
127 | ||
128 | current = directory.get_child(i.get_name()); | |
129 | ||
130 | if (load_password()) | |
131 | break; | |
132 | ||
133 | current = null; | |
134 | } | |
135 | } | |
136 | ||
137 | if (current == null) | |
138 | set_visible(false); | |
139 | ||
140 | } | |
141 | ||
efb3237e | 142 | bool load_password() throws GLib.Error { |
490aed58 LP |
143 | |
144 | KeyFile key_file = new KeyFile(); | |
145 | ||
146 | try { | |
147 | timespec ts; | |
148 | ||
149 | key_file.load_from_file(current.get_path(), KeyFileFlags.NONE); | |
150 | ||
151 | string not_after_as_string = key_file.get_string("Ask", "NotAfter"); | |
152 | ||
153 | clock_gettime(1, out ts); | |
154 | uint64 now = (ts.tv_sec * 1000000) + (ts.tv_nsec / 1000); | |
155 | ||
156 | uint64 not_after; | |
157 | if (not_after_as_string.scanf("%llu", out not_after) != 1) | |
158 | return false; | |
159 | ||
160 | if (not_after < now) | |
161 | return false; | |
162 | ||
163 | socket = key_file.get_string("Ask", "Socket"); | |
164 | } catch (GLib.Error e) { | |
165 | return false; | |
166 | } | |
167 | ||
168 | try { | |
169 | message = key_file.get_string("Ask", "Message").compress(); | |
170 | } catch (GLib.Error e) { | |
171 | message = "Please Enter System Password!"; | |
172 | } | |
173 | set_tooltip_text(message); | |
174 | ||
175 | try { | |
176 | icon = key_file.get_string("Ask", "Icon"); | |
177 | } catch (GLib.Error e) { | |
178 | icon = "dialog-password"; | |
179 | } | |
180 | set_from_icon_name(icon); | |
181 | ||
182 | set_visible(true); | |
efb3237e | 183 | |
230e5a3f | 184 | Notification n = new Notification(title, message, icon); |
efb3237e LP |
185 | n.set_timeout(5000); |
186 | n.show(); | |
187 | ||
490aed58 LP |
188 | return true; |
189 | } | |
190 | ||
191 | void status_icon_activate() throws GLib.Error { | |
192 | ||
193 | if (current == null) | |
194 | return; | |
195 | ||
196 | if (password_dialog != null) { | |
197 | password_dialog.present(); | |
198 | return; | |
199 | } | |
200 | ||
201 | password_dialog = new PasswordDialog(message, icon); | |
202 | ||
203 | int result = password_dialog.run(); | |
204 | string password = password_dialog.entry.get_text(); | |
205 | ||
206 | password_dialog.destroy(); | |
207 | password_dialog = null; | |
208 | ||
209 | if (result == ResponseType.REJECT || | |
210 | result == ResponseType.DELETE_EVENT) | |
211 | return; | |
212 | ||
213 | int to_process; | |
214 | ||
215 | Process.spawn_async_with_pipes( | |
216 | null, | |
217 | { "/usr/bin/pkexec", "/lib/systemd/systemd-reply-password", result == ResponseType.OK ? "1" : "0", socket }, | |
218 | null, | |
219 | 0, | |
220 | null, | |
221 | null, | |
222 | out to_process, | |
223 | null, | |
224 | null); | |
225 | ||
226 | OutputStream stream = new UnixOutputStream(to_process, true); | |
227 | ||
230e5a3f | 228 | stream.write(password.data, null); |
490aed58 LP |
229 | } |
230 | } | |
231 | ||
232 | static const OptionEntry entries[] = { | |
233 | { null } | |
234 | }; | |
235 | ||
236 | void show_error(string e) { | |
237 | var m = new MessageDialog(null, 0, MessageType.ERROR, ButtonsType.CLOSE, "%s", e); | |
238 | m.run(); | |
239 | m.destroy(); | |
240 | } | |
241 | ||
242 | int main(string[] args) { | |
243 | try { | |
244 | Gtk.init_with_args(ref args, "[OPTION...]", entries, "systemd-ask-password-agent"); | |
efb3237e | 245 | Notify.init("Password Agent"); |
490aed58 LP |
246 | |
247 | MyStatusIcon i = new MyStatusIcon(); | |
248 | Gtk.main(); | |
249 | ||
250 | } catch (DBus.Error e) { | |
251 | show_error(e.message); | |
252 | } catch (GLib.Error e) { | |
253 | show_error(e.message); | |
254 | } | |
255 | ||
256 | return 0; | |
257 | } |