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