]> git.ipfire.org Git - people/ms/systemd.git/blob - systemadm.vala
systemadm: implement basic control UI systemadm
[people/ms/systemd.git] / systemadm.vala
1 using Gtk;
2 using GLib;
3 using DBus;
4 using Pango;
5
6 public class LeftLabel : Label {
7 public LeftLabel(string? text = null) {
8 if (text != null)
9 set_markup("<b>%s</b>".printf(text));
10 set_alignment(1, 0);
11 set_padding(6, 0);
12 }
13 }
14
15 public class RightLabel : Label {
16 public RightLabel(string? text = null) {
17 set_text_or_na(text);
18 set_alignment(0, 1);
19 set_ellipsize(EllipsizeMode.START);
20 set_selectable(true);
21 }
22
23 public void set_text_or_na(string? text = null) {
24 if (text == null || text == "")
25 set_markup("<i>n/a</i>");
26 else
27 set_text(text);
28 }
29 }
30
31 public class MainWindow : Window {
32
33 private TreeView unit_view;
34 private TreeView job_view;
35
36 private ListStore unit_model;
37 private ListStore job_model;
38
39 private Button start_button;
40 private Button stop_button;
41 private Button restart_button;
42 private Button reload_button;
43 private Button cancel_button;
44
45 private Connection bus;
46 private Manager manager;
47
48 private RightLabel unit_id_label;
49 private RightLabel unit_description_label;
50 private RightLabel unit_load_state_label;
51 private RightLabel unit_active_state_label;
52 private RightLabel unit_load_path_label;
53 private RightLabel unit_active_enter_timestamp_label;
54 private RightLabel unit_active_exit_timestamp_label;
55 private RightLabel unit_can_start_label;
56 private RightLabel unit_can_reload_label;
57
58 private RightLabel job_id_label;
59 private RightLabel job_state_label;
60 private RightLabel job_type_label;
61
62 public MainWindow() throws DBus.Error {
63 title = "systemdadm";
64 position = WindowPosition.CENTER;
65 set_default_size(1000, 700);
66 set_border_width(12);
67 destroy.connect(Gtk.main_quit);
68
69 Notebook notebook = new Notebook();
70 add(notebook);
71
72 Box unit_vbox = new VBox(false, 6);
73 notebook.append_page(unit_vbox, new Label("Units"));
74 unit_vbox.set_border_width(12);
75
76 Box job_vbox = new VBox(false, 6);
77 notebook.append_page(job_vbox, new Label("Jobs"));
78 job_vbox.set_border_width(12);
79
80
81 unit_model = new ListStore(6, typeof(string), typeof(string), typeof(string), typeof(string), typeof(string), typeof(string), typeof(string));
82 job_model = new ListStore(5, typeof(string), typeof(string), typeof(string), typeof(string), typeof(string), typeof(string));
83
84 unit_view = new TreeView.with_model(unit_model);
85 job_view = new TreeView.with_model(job_model);
86
87 unit_view.cursor_changed.connect(unit_changed);
88 job_view.cursor_changed.connect(job_changed);
89
90 unit_view.insert_column_with_attributes(-1, "Unit", new CellRendererText(), "text", 0);
91 unit_view.insert_column_with_attributes(-1, "Description", new CellRendererText(), "text", 1);
92 unit_view.insert_column_with_attributes(-1, "Load State", new CellRendererText(), "text", 2);
93 unit_view.insert_column_with_attributes(-1, "Active State", new CellRendererText(), "text", 3);
94 unit_view.insert_column_with_attributes(-1, "Job", new CellRendererText(), "text", 4);
95
96 job_view.insert_column_with_attributes(-1, "Job", new CellRendererText(), "text", 0);
97 job_view.insert_column_with_attributes(-1, "Unit", new CellRendererText(), "text", 1);
98 job_view.insert_column_with_attributes(-1, "Type", new CellRendererText(), "text", 2);
99 job_view.insert_column_with_attributes(-1, "State", new CellRendererText(), "text", 3);
100
101 ScrolledWindow scroll = new ScrolledWindow(null, null);
102 scroll.set_policy(PolicyType.AUTOMATIC, PolicyType.AUTOMATIC);
103 scroll.set_shadow_type(ShadowType.IN);
104 scroll.add(unit_view);
105 unit_vbox.pack_start(scroll, true, true, 0);
106
107 scroll = new ScrolledWindow(null, null);
108 scroll.set_policy(PolicyType.AUTOMATIC, PolicyType.AUTOMATIC);
109 scroll.set_shadow_type(ShadowType.IN);
110 scroll.add(job_view);
111 job_vbox.pack_start(scroll, true, true, 0);
112
113 unit_id_label = new RightLabel();
114 unit_description_label = new RightLabel();
115 unit_load_state_label = new RightLabel();
116 unit_active_state_label = new RightLabel();
117 unit_load_path_label = new RightLabel();
118 unit_active_enter_timestamp_label = new RightLabel();
119 unit_active_exit_timestamp_label = new RightLabel();
120 unit_can_start_label = new RightLabel();
121 unit_can_reload_label = new RightLabel();
122
123 job_id_label = new RightLabel();
124 job_state_label = new RightLabel();
125 job_type_label = new RightLabel();
126
127 Table unit_table = new Table(9, 2, false);
128 unit_table.set_row_spacings(6);
129 unit_table.set_border_width(12);
130 unit_vbox.pack_start(unit_table, false, true, 0);
131
132 Table job_table = new Table(2, 2, false);
133 job_table.set_row_spacings(6);
134 job_table.set_border_width(12);
135 job_vbox.pack_start(job_table, false, true, 0);
136
137 unit_table.attach(new LeftLabel("Id:"), 0, 1, 0, 1, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
138 unit_table.attach(unit_id_label, 1, 2, 0, 1, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
139 unit_table.attach(new LeftLabel("Description:"), 0, 1, 1, 2, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
140 unit_table.attach(unit_description_label, 1, 2, 1, 2, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
141 unit_table.attach(new LeftLabel("Load State:"), 0, 1, 2, 3, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
142 unit_table.attach(unit_load_state_label, 1, 2, 2, 3, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
143 unit_table.attach(new LeftLabel("Active State:"), 0, 1, 3, 4, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
144 unit_table.attach(unit_active_state_label, 1, 2, 3, 4, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
145 unit_table.attach(new LeftLabel("Load Path:"), 0, 1, 4, 5, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
146 unit_table.attach(unit_load_path_label, 1, 2, 4, 5, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
147 unit_table.attach(new LeftLabel("Active Enter Timestamp:"), 0, 1, 5, 6, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
148 unit_table.attach(unit_active_enter_timestamp_label, 1, 2, 5, 6, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
149 unit_table.attach(new LeftLabel("Active Exit Timestamp:"), 0, 1, 6, 7, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
150 unit_table.attach(unit_active_exit_timestamp_label, 1, 2, 6, 7, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
151 unit_table.attach(new LeftLabel("Can Start/Stop:"), 0, 1, 7, 8, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
152 unit_table.attach(unit_can_start_label, 1, 2, 7, 8, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
153 unit_table.attach(new LeftLabel("Can Reload:"), 0, 1, 8, 9, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
154 unit_table.attach(unit_can_reload_label, 1, 2, 8, 9, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
155
156 job_table.attach(new LeftLabel("Id:"), 0, 1, 0, 1, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
157 job_table.attach(job_id_label, 1, 2, 0, 1, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
158 job_table.attach(new LeftLabel("State:"), 0, 1, 1, 2, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
159 job_table.attach(job_state_label, 1, 2, 1, 2, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
160 job_table.attach(new LeftLabel("Type:"), 0, 1, 2, 3, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
161 job_table.attach(job_type_label, 1, 2, 2, 3, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
162
163 ButtonBox bbox = new HButtonBox();
164 bbox.set_layout(ButtonBoxStyle.START);
165 bbox.set_spacing(6);
166 unit_vbox.pack_start(bbox, false, true, 0);
167
168 start_button = new Button.with_mnemonic("_Start");
169 stop_button = new Button.with_mnemonic("Sto_p");
170 reload_button = new Button.with_mnemonic("_Reload");
171 restart_button = new Button.with_mnemonic("Res_tart");
172
173 start_button.clicked.connect(on_start);
174 stop_button.clicked.connect(on_stop);
175 reload_button.clicked.connect(on_reload);
176 restart_button.clicked.connect(on_restart);
177
178 bbox.pack_start(start_button, false, true, 0);
179 bbox.pack_start(stop_button, false, true, 0);
180 bbox.pack_start(restart_button, false, true, 0);
181 bbox.pack_start(reload_button, false, true, 0);
182
183 bbox = new HButtonBox();
184 bbox.set_layout(ButtonBoxStyle.START);
185 bbox.set_spacing(6);
186 job_vbox.pack_start(bbox, false, true, 0);
187
188 cancel_button = new Button.with_mnemonic("_Cancel");
189
190 cancel_button.clicked.connect(on_cancel);
191
192 bbox.pack_start(cancel_button, false, true, 0);
193
194 bus = Bus.get(BusType.SESSION);
195
196 manager = bus.get_object (
197 "org.freedesktop.systemd1",
198 "/org/freedesktop/systemd1",
199 "org.freedesktop.systemd1") as Manager;
200
201 clear_unit();
202 populate_unit_model();
203 populate_job_model();
204 }
205
206 public void populate_unit_model() throws DBus.Error {
207 unit_model.clear();
208
209 var list = manager.list_units();
210
211 foreach (var i in list) {
212 TreeIter iter;
213
214 unit_model.append(out iter);
215 unit_model.set(iter,
216 0, i.id,
217 1, i.description,
218 2, i.load_state,
219 3, i.active_state,
220 4, i.job_type != "" ? "→ %s".printf(i.job_type) : "",
221 5, i.unit_path);
222 }
223 }
224
225 public void populate_job_model() throws DBus.Error {
226 job_model.clear();
227
228 var list = manager.list_jobs();
229
230 foreach (var i in list) {
231 TreeIter iter;
232
233 job_model.append(out iter);
234 job_model.set(iter,
235 0, "%u".printf(i.id),
236 1, i.name,
237 2, "→ %s".printf(i.type),
238 3, i.state,
239 4, i.job_path);
240 }
241 }
242
243 public Unit? get_current_unit() {
244 TreePath p;
245 unit_view.get_cursor(out p, null);
246
247 if (p == null)
248 return null;
249
250 TreeIter iter;
251 string path;
252
253 unit_model.get_iter(out iter, p);
254 unit_model.get(iter, 5, out path);
255
256 return bus.get_object (
257 "org.freedesktop.systemd1",
258 path,
259 "org.freedesktop.systemd1.Unit") as Unit;
260 }
261
262 public void unit_changed() {
263 Unit u = get_current_unit();
264
265 if (u == null)
266 clear_unit();
267 else
268 show_unit(u);
269 }
270
271 public void clear_unit() {
272 start_button.set_sensitive(false);
273 stop_button.set_sensitive(false);
274 reload_button.set_sensitive(false);
275 restart_button.set_sensitive(false);
276
277 unit_id_label.set_text_or_na();
278 unit_description_label.set_text_or_na();
279 unit_load_state_label.set_text_or_na();
280 unit_active_state_label.set_text_or_na();
281 unit_load_path_label.set_text_or_na();
282 unit_active_enter_timestamp_label.set_text_or_na();
283 unit_active_exit_timestamp_label.set_text_or_na();
284 unit_can_reload_label.set_text_or_na();
285 unit_can_start_label.set_text_or_na();
286 }
287
288 public void show_unit(Unit unit) {
289 unit_id_label.set_text_or_na(unit.id);
290 unit_description_label.set_text_or_na(unit.description);
291 unit_load_state_label.set_text_or_na(unit.load_state);
292 unit_active_state_label.set_text_or_na(unit.active_state);
293 unit_load_path_label.set_text_or_na(unit.load_path);
294
295 uint64 t = unit.active_enter_timestamp;
296 if (t > 0) {
297 TimeVal tv = { (long) (t / 1000000), (long) (t % 1000000) };
298 unit_active_enter_timestamp_label.set_text_or_na(tv.to_iso8601());
299 } else
300 unit_active_enter_timestamp_label.set_text_or_na();
301
302 t = unit.active_exit_timestamp;
303 if (t > 0) {
304 TimeVal tv = { (long) (t / 1000000), (long) (t % 1000000) };
305 unit_active_exit_timestamp_label.set_text_or_na(tv.to_iso8601());
306 } else
307 unit_active_exit_timestamp_label.set_text_or_na();
308
309 bool b = unit.can_start;
310 start_button.set_sensitive(b);
311 stop_button.set_sensitive(b);
312 restart_button.set_sensitive(b);
313 unit_can_start_label.set_text_or_na(b ? "Yes" : "No");
314
315 b = unit.can_reload;
316 reload_button.set_sensitive(b);
317 unit_can_reload_label.set_text_or_na(b ? "Yes" : "No");
318 }
319
320 public Job? get_current_job() {
321 TreePath p;
322 job_view.get_cursor(out p, null);
323
324 if (p == null)
325 return null;
326
327 TreeIter iter;
328 string path;
329
330 job_model.get_iter(out iter, p);
331 job_model.get(iter, 4, out path);
332
333 return bus.get_object (
334 "org.freedesktop.systemd1",
335 path,
336 "org.freedesktop.systemd1.Job") as Job;
337 }
338
339 public void job_changed() {
340 Job j = get_current_job();
341
342 if (j == null)
343 clear_job();
344 else
345 show_job(j);
346 }
347
348 public void clear_job() {
349 job_id_label.set_text_or_na();
350 job_state_label.set_text_or_na();
351 job_type_label.set_text_or_na();
352 }
353
354 public void show_job(Job job) {
355 job_id_label.set_text_or_na("%u".printf(job.id));
356 job_state_label.set_text_or_na(job.state);
357 job_type_label.set_text_or_na(job.job_type);
358 }
359
360 public void on_start() {
361 Unit u = get_current_unit();
362
363 if (u == null)
364 return;
365
366 try {
367 u.start("replace");
368 } catch (DBus.Error e) {
369 message("%s", e.message);
370 }
371 }
372
373 public void on_stop() {
374 Unit u = get_current_unit();
375
376 if (u == null)
377 return;
378
379 try {
380 u.stop("replace");
381 } catch (DBus.Error e) {
382 message("%s", e.message);
383 }
384 }
385
386 public void on_reload() {
387 Unit u = get_current_unit();
388
389 if (u == null)
390 return;
391
392 try {
393 u.reload("replace");
394 } catch (DBus.Error e) {
395 message("%s", e.message);
396 }
397 }
398
399 public void on_restart() {
400 Unit u = get_current_unit();
401
402 if (u == null)
403 return;
404
405 try {
406 u.restart("replace");
407 } catch (DBus.Error e) {
408 message("%s", e.message);
409 }
410 }
411
412 public void on_cancel() {
413 Job j = get_current_job();
414
415 if (j == null)
416 return;
417
418 try {
419 j.cancel();
420 } catch (DBus.Error e) {
421 message("%s", e.message);
422 }
423 }
424 }
425
426 int main (string[] args) {
427 Gtk.init(ref args);
428
429 try {
430 MainWindow window = new MainWindow();
431 window.show_all();
432 } catch (DBus.Error e) {
433 message("%s", e.message);
434 }
435
436 Gtk.main();
437 return 0;
438 }