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