chiark / gitweb /
3f1cf9e1138bf9d0acebed3ee4542afd3f57f096
[elogind.git] / src / 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(0, 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, 0);
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         public void set_markup_or_na(string? text = null) {
52                 if (text == null || text == "")
53                         set_markup("<i>n/a</i>");
54                 else
55                         set_markup(text);
56         }
57 }
58
59 public class MainWindow : Window {
60
61         private string? current_unit_id;
62         private uint32 current_job_id;
63
64         private TreeView unit_view;
65         private TreeView job_view;
66
67         private ListStore unit_model;
68         private ListStore job_model;
69
70         private Button start_button;
71         private Button stop_button;
72         private Button restart_button;
73         private Button reload_button;
74         private Button cancel_button;
75
76         private Entry unit_load_entry;
77         private Button unit_load_button;
78
79         private Button server_snapshot_button;
80         private Button server_reload_button;
81
82         private Connection bus;
83         private Manager manager;
84
85         private RightLabel unit_id_label;
86         private RightLabel unit_aliases_label;
87         private RightLabel unit_dependency_label;
88         private RightLabel unit_description_label;
89         private RightLabel unit_load_state_label;
90         private RightLabel unit_active_state_label;
91         private RightLabel unit_sub_state_label;
92         private RightLabel unit_fragment_path_label;
93         private RightLabel unit_active_enter_timestamp_label;
94         private RightLabel unit_active_exit_timestamp_label;
95         private RightLabel unit_can_start_label;
96         private RightLabel unit_can_reload_label;
97         private RightLabel unit_cgroup_label;
98
99         private RightLabel job_id_label;
100         private RightLabel job_state_label;
101         private RightLabel job_type_label;
102
103         private ComboBox unit_type_combo_box;
104
105         public MainWindow() throws DBus.Error {
106                 title = session ? "systemd Session Manager" : "systemd System Manager";
107                 position = WindowPosition.CENTER;
108                 set_default_size(1000, 700);
109                 set_border_width(12);
110                 destroy += Gtk.main_quit;
111
112                 Notebook notebook = new Notebook();
113                 add(notebook);
114
115                 Box unit_vbox = new VBox(false, 12);
116                 notebook.append_page(unit_vbox, new Label("Units"));
117                 unit_vbox.set_border_width(12);
118
119                 Box job_vbox = new VBox(false, 12);
120                 notebook.append_page(job_vbox, new Label("Jobs"));
121                 job_vbox.set_border_width(12);
122
123                 unit_type_combo_box = new ComboBox.text();
124                 Box type_hbox = new HBox(false, 6);
125                 type_hbox.pack_start(unit_type_combo_box, false, false, 0);
126                 unit_vbox.pack_start(type_hbox, false, false, 0);
127
128                 unit_type_combo_box.append_text("Show All Units");
129                 unit_type_combo_box.append_text("Show Only Live Units");
130                 unit_type_combo_box.append_text("Services");
131                 unit_type_combo_box.append_text("Sockets");
132                 unit_type_combo_box.append_text("Devices");
133                 unit_type_combo_box.append_text("Mounts");
134                 unit_type_combo_box.append_text("Automounts");
135                 unit_type_combo_box.append_text("Targets");
136                 unit_type_combo_box.append_text("Snapshots");
137                 unit_type_combo_box.set_active(1);
138                 unit_type_combo_box.changed += unit_type_changed;
139
140                 unit_load_entry = new Entry();
141                 unit_load_button = new Button.with_mnemonic("_Load");
142                 unit_load_button.set_sensitive(false);
143
144                 unit_load_entry.changed += on_unit_load_entry_changed;
145                 unit_load_entry.activate += on_unit_load;
146                 unit_load_button.clicked += on_unit_load;
147
148                 Box unit_load_hbox = new HBox(false, 6);
149                 unit_load_hbox.pack_start(unit_load_entry, false, true, 0);
150                 unit_load_hbox.pack_start(unit_load_button, false, true, 0);
151
152                 server_snapshot_button = new Button.with_mnemonic("Take S_napshot");
153                 server_reload_button = new Button.with_mnemonic("Reload _Configuration");
154
155                 server_snapshot_button.clicked += on_server_snapshot;
156                 server_reload_button.clicked += on_server_reload;
157
158                 type_hbox.pack_end(server_snapshot_button, false, true, 0);
159                 type_hbox.pack_end(server_reload_button, false, true, 0);
160                 type_hbox.pack_end(unit_load_hbox, false, true, 24);
161
162                 unit_model = new ListStore(7, typeof(string), typeof(string), typeof(string), typeof(string), typeof(string), typeof(string), typeof(Unit));
163                 job_model = new ListStore(6, typeof(string), typeof(string), typeof(string), typeof(string), typeof(Job), typeof(uint32));
164
165                 TreeModelFilter unit_model_filter;
166                 unit_model_filter = new TreeModelFilter(unit_model, null);
167                 unit_model_filter.set_visible_func(unit_filter);
168
169                 unit_view = new TreeView.with_model(unit_model_filter);
170                 job_view = new TreeView.with_model(job_model);
171
172                 unit_view.cursor_changed += unit_changed;
173                 job_view.cursor_changed += job_changed;
174
175                 unit_view.insert_column_with_attributes(-1, "Load State", new CellRendererText(), "text", 2);
176                 unit_view.insert_column_with_attributes(-1, "Active State", new CellRendererText(), "text", 3);
177                 unit_view.insert_column_with_attributes(-1, "Unit State", new CellRendererText(), "text", 4);
178                 unit_view.insert_column_with_attributes(-1, "Unit", new CellRendererText(), "text", 0);
179                 unit_view.insert_column_with_attributes(-1, "Job", new CellRendererText(), "text", 5);
180
181                 job_view.insert_column_with_attributes(-1, "Job", new CellRendererText(), "text", 0);
182                 job_view.insert_column_with_attributes(-1, "Unit", new CellRendererText(), "text", 1);
183                 job_view.insert_column_with_attributes(-1, "Type", new CellRendererText(), "text", 2);
184                 job_view.insert_column_with_attributes(-1, "State", new CellRendererText(), "text", 3);
185
186                 ScrolledWindow scroll = new ScrolledWindow(null, null);
187                 scroll.set_policy(PolicyType.AUTOMATIC, PolicyType.AUTOMATIC);
188                 scroll.set_shadow_type(ShadowType.IN);
189                 scroll.add(unit_view);
190                 unit_vbox.pack_start(scroll, true, true, 0);
191
192                 scroll = new ScrolledWindow(null, null);
193                 scroll.set_policy(PolicyType.AUTOMATIC, PolicyType.AUTOMATIC);
194                 scroll.set_shadow_type(ShadowType.IN);
195                 scroll.add(job_view);
196                 job_vbox.pack_start(scroll, true, true, 0);
197
198                 unit_id_label = new RightLabel();
199                 unit_aliases_label = new RightLabel();
200                 unit_dependency_label = new RightLabel();
201                 unit_description_label = new RightLabel();
202                 unit_load_state_label = new RightLabel();
203                 unit_active_state_label = new RightLabel();
204                 unit_sub_state_label = new RightLabel();
205                 unit_fragment_path_label = new RightLabel();
206                 unit_active_enter_timestamp_label = new RightLabel();
207                 unit_active_exit_timestamp_label = new RightLabel();
208                 unit_can_start_label = new RightLabel();
209                 unit_can_reload_label = new RightLabel();
210                 unit_cgroup_label = new RightLabel();
211
212                 job_id_label = new RightLabel();
213                 job_state_label = new RightLabel();
214                 job_type_label = new RightLabel();
215
216                 unit_dependency_label.set_track_visited_links(false);
217                 unit_dependency_label.set_selectable(false);
218                 unit_dependency_label.activate_link += on_activate_link;
219
220                 unit_fragment_path_label.set_track_visited_links(false);
221
222                 Table unit_table = new Table(8, 6, false);
223                 unit_table.set_row_spacings(6);
224                 unit_table.set_border_width(0);
225                 unit_vbox.pack_start(unit_table, false, true, 0);
226
227                 Table job_table = new Table(2, 2, false);
228                 job_table.set_row_spacings(6);
229                 job_table.set_border_width(0);
230                 job_vbox.pack_start(job_table, false, true, 0);
231
232                 unit_table.attach(new LeftLabel("Id:"),                     0, 1, 0, 1, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
233                 unit_table.attach(unit_id_label,                            1, 6, 0, 1, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
234                 unit_table.attach(new LeftLabel("Aliases:"),                0, 1, 1, 2, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
235                 unit_table.attach(unit_aliases_label,                       1, 6, 1, 2, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
236                 unit_table.attach(new LeftLabel("Description:"),            0, 1, 2, 3, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
237                 unit_table.attach(unit_description_label,                   1, 6, 2, 3, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
238                 unit_table.attach(new LeftLabel("Dependencies:"),           0, 1, 3, 4, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
239                 unit_table.attach(unit_dependency_label,                    1, 6, 3, 4, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
240                 unit_table.attach(new LeftLabel("Fragment Path:"),          0, 1, 4, 5, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
241                 unit_table.attach(unit_fragment_path_label,                 1, 6, 4, 5, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
242                 unit_table.attach(new LeftLabel("Control Group:"),          0, 1, 5, 6, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
243                 unit_table.attach(unit_cgroup_label,                        1, 6, 5, 6, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
244
245                 unit_table.attach(new LeftLabel("Load State:"),             0, 1, 6, 7, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
246                 unit_table.attach(unit_load_state_label,                    1, 2, 6, 7, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
247                 unit_table.attach(new LeftLabel("Active State:"),           0, 1, 7, 8, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
248                 unit_table.attach(unit_active_state_label,                  1, 2, 7, 8, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
249                 unit_table.attach(new LeftLabel("Unit State:"),             0, 1, 8, 9, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
250                 unit_table.attach(unit_sub_state_label,                     1, 2, 8, 9, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
251
252                 unit_table.attach(new LeftLabel("Active Enter Timestamp:"), 2, 3, 7, 8, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
253                 unit_table.attach(unit_active_enter_timestamp_label,        3, 4, 7, 8, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
254                 unit_table.attach(new LeftLabel("Active Exit Timestamp:"),  2, 3, 8, 9, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
255                 unit_table.attach(unit_active_exit_timestamp_label,         3, 4, 8, 9, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
256
257                 unit_table.attach(new LeftLabel("Can Start/Stop:"),         4, 5, 7, 8, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
258                 unit_table.attach(unit_can_start_label,                     5, 6, 7, 8, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
259                 unit_table.attach(new LeftLabel("Can Reload:"),             4, 5, 8, 9, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
260                 unit_table.attach(unit_can_reload_label,                    5, 6, 8, 9, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
261
262                 job_table.attach(new LeftLabel("Id:"),                      0, 1, 0, 1, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
263                 job_table.attach(job_id_label,                              1, 2, 0, 1, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
264                 job_table.attach(new LeftLabel("State:"),                   0, 1, 1, 2, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
265                 job_table.attach(job_state_label,                           1, 2, 1, 2, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
266                 job_table.attach(new LeftLabel("Type:"),                    0, 1, 2, 3, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
267                 job_table.attach(job_type_label,                            1, 2, 2, 3, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
268
269                 ButtonBox bbox = new HButtonBox();
270                 bbox.set_layout(ButtonBoxStyle.START);
271                 bbox.set_spacing(6);
272                 unit_vbox.pack_start(bbox, false, true, 0);
273
274                 start_button = new Button.with_mnemonic("_Start");
275                 stop_button = new Button.with_mnemonic("Sto_p");
276                 reload_button = new Button.with_mnemonic("_Reload");
277                 restart_button = new Button.with_mnemonic("Res_tart");
278
279                 start_button.clicked += on_start;
280                 stop_button.clicked += on_stop;
281                 reload_button.clicked += on_reload;
282                 restart_button.clicked += on_restart;
283
284                 bbox.pack_start(start_button, false, true, 0);
285                 bbox.pack_start(stop_button, false, true, 0);
286                 bbox.pack_start(restart_button, false, true, 0);
287                 bbox.pack_start(reload_button, false, true, 0);
288
289                 bbox = new HButtonBox();
290                 bbox.set_layout(ButtonBoxStyle.START);
291                 bbox.set_spacing(6);
292                 job_vbox.pack_start(bbox, false, true, 0);
293
294                 cancel_button = new Button.with_mnemonic("_Cancel");
295
296                 cancel_button.clicked += on_cancel;
297
298                 bbox.pack_start(cancel_button, false, true, 0);
299
300                 bus = Bus.get(session ? BusType.SESSION : BusType.SYSTEM);
301
302                 manager = bus.get_object(
303                                 "org.freedesktop.systemd1",
304                                 "/org/freedesktop/systemd1",
305                                 "org.freedesktop.systemd1.Manager") as Manager;
306
307                 manager.unit_new += on_unit_new;
308                 manager.job_new += on_job_new;
309                 manager.unit_removed += on_unit_removed;
310                 manager.job_removed += on_job_removed;
311
312                 manager.subscribe();
313
314                 clear_unit();
315                 clear_job();
316                 populate_unit_model();
317                 populate_job_model();
318         }
319
320         public void populate_unit_model() throws DBus.Error {
321                 unit_model.clear();
322
323                 var list = manager.list_units();
324
325                 foreach (var i in list) {
326                         TreeIter iter;
327
328                         Unit u = bus.get_object(
329                                         "org.freedesktop.systemd1",
330                                         i.unit_path,
331                                         "org.freedesktop.systemd1.Unit") as Unit;
332
333                         u.changed += on_unit_changed;
334
335                         unit_model.append(out iter);
336                         unit_model.set(iter,
337                                        0, i.id,
338                                        1, i.description,
339                                        2, i.load_state,
340                                        3, i.active_state,
341                                        4, i.sub_state,
342                                        5, i.job_type != "" ? "→ %s".printf(i.job_type) : "",
343                                        6, u);
344                 }
345         }
346
347         public void populate_job_model() throws DBus.Error {
348                 job_model.clear();
349
350                 var list = manager.list_jobs();
351
352                 foreach (var i in list) {
353                         TreeIter iter;
354
355                         Job j = bus.get_object(
356                                         "org.freedesktop.systemd1",
357                                         i.job_path,
358                                         "org.freedesktop.systemd1.Job") as Job;
359
360                         j.changed += on_job_changed;
361
362                         job_model.append(out iter);
363                         job_model.set(iter,
364                                       0, "%u".printf(i.id),
365                                       1, i.name,
366                                       2, "→ %s".printf(i.type),
367                                       3, i.state,
368                                       4, j,
369                                       5, i.id);
370                 }
371         }
372
373         public Unit? get_current_unit() {
374                 TreePath p;
375                 unit_view.get_cursor(out p, null);
376
377                 if (p == null)
378                         return null;
379
380                 TreeModel model = unit_view.get_model();
381                 TreeIter iter;
382                 Unit u;
383
384                 model.get_iter(out iter, p);
385                 model.get(iter, 6, out u);
386
387                 return u;
388         }
389
390         public void unit_changed() {
391                 Unit u = get_current_unit();
392
393                 if (u == null)
394                         clear_unit();
395                 else
396                         show_unit(u);
397         }
398
399         public void clear_unit() {
400                 current_unit_id = null;
401
402                 start_button.set_sensitive(false);
403                 stop_button.set_sensitive(false);
404                 reload_button.set_sensitive(false);
405                 restart_button.set_sensitive(false);
406
407                 unit_id_label.set_text_or_na();
408                 unit_aliases_label.set_text_or_na();
409                 unit_description_label.set_text_or_na();
410                 unit_description_label.set_text_or_na();
411                 unit_load_state_label.set_text_or_na();
412                 unit_active_state_label.set_text_or_na();
413                 unit_sub_state_label.set_text_or_na();
414                 unit_fragment_path_label.set_text_or_na();
415                 unit_active_enter_timestamp_label.set_text_or_na();
416                 unit_active_exit_timestamp_label.set_text_or_na();
417                 unit_can_reload_label.set_text_or_na();
418                 unit_can_start_label.set_text_or_na();
419                 unit_cgroup_label.set_text_or_na();
420         }
421
422         public string make_dependency_string(string? prefix, string word, string[] dependencies) {
423                 bool first = true;
424                 string r;
425
426                 if (prefix == null)
427                         r = "";
428                 else
429                         r = prefix;
430
431                 foreach (string i in dependencies) {
432                         if (r != "")
433                                 r += first ? "\n" : ",";
434
435                         if (first) {
436                                 r += word;
437                                 first = false;
438                         }
439
440                         r += " <a href=\"" + i + "\">" + i + "</a>";
441                 }
442
443                 return r;
444         }
445
446         public void show_unit(Unit unit) {
447                 current_unit_id = unit.id;
448
449                 unit_id_label.set_text_or_na(current_unit_id);
450
451                 string a = "";
452                 foreach (string i in unit.names) {
453                         if (i == current_unit_id)
454                                 continue;
455
456                         if (a == "")
457                                 a = i;
458                         else
459                                 a += "\n" + i;
460                 }
461
462                 unit_aliases_label.set_text_or_na(a);
463
464                 string[]
465                         requires = unit.requires,
466                         requires_overridable = unit.requires_overridable,
467                         requisite = unit.requisite,
468                         requisite_overridable = unit.requisite_overridable,
469                         wants = unit.wants,
470                         required_by = unit.required_by,
471                         required_by_overridable = unit.required_by_overridable,
472                         wanted_by = unit.wanted_by,
473                         conflicts = unit.conflicts,
474                         before = unit.before,
475                         after = unit.after;
476
477                 unit_dependency_label.set_markup_or_na(
478                                 make_dependency_string(
479                                 make_dependency_string(
480                                 make_dependency_string(
481                                 make_dependency_string(
482                                 make_dependency_string(
483                                 make_dependency_string(
484                                 make_dependency_string(
485                                 make_dependency_string(
486                                 make_dependency_string(
487                                 make_dependency_string(
488                                 make_dependency_string(null,
489                                 "requires", requires),
490                                 "overridable requires", requires_overridable),
491                                 "requisite", requisite),
492                                 "overridable requisite", requisite_overridable),
493                                 "wants", wants),
494                                 "conflicts", conflicts),
495                                 "required by", required_by),
496                                 "overridable required by", required_by_overridable),
497                                 "wanted by", wanted_by),
498                                 "after", after),
499                                 "before", before));
500
501                 unit_description_label.set_text_or_na(unit.description);
502                 unit_load_state_label.set_text_or_na(unit.load_state);
503                 unit_active_state_label.set_text_or_na(unit.active_state);
504                 unit_sub_state_label.set_text_or_na(unit.sub_state);
505                 if (unit.fragment_path != null)
506                         unit_fragment_path_label.set_markup_or_na("<a href=\"file://" + unit.fragment_path +"\">" + unit.fragment_path + "</a>" );
507                 else
508                         unit_fragment_path_label.set_markup_or_na(null);
509
510                 uint64 t = unit.active_enter_timestamp;
511                 if (t > 0) {
512                         Time timestamp = Time.local((time_t) (t / 1000000));
513                         unit_active_enter_timestamp_label.set_text_or_na(timestamp.format("%a, %d %b %Y %H:%M:%S %z"));
514                 } else
515                         unit_active_enter_timestamp_label.set_text_or_na();
516
517                 t = unit.active_exit_timestamp;
518                 if (t > 0) {
519                         Time timestamp = Time.local((time_t) (t / 1000000));
520                         unit_active_exit_timestamp_label.set_text_or_na(timestamp.format("%a, %d %b %Y %H:%M:%S %z"));
521                 } else
522                         unit_active_exit_timestamp_label.set_text_or_na();
523
524                 bool b = unit.can_start;
525                 start_button.set_sensitive(b);
526                 stop_button.set_sensitive(b);
527                 restart_button.set_sensitive(b);
528                 unit_can_start_label.set_text_or_na(b ? "Yes" : "No");
529
530                 b = unit.can_reload;
531                 reload_button.set_sensitive(b);
532                 unit_can_reload_label.set_text_or_na(b ? "Yes" : "No");
533
534                 unit_cgroup_label.set_text_or_na(unit.default_control_group);
535         }
536
537         public Job? get_current_job() {
538                 TreePath p;
539                 job_view.get_cursor(out p, null);
540
541                 if (p == null)
542                         return null;
543
544                 TreeIter iter;
545                 TreeModel model = job_view.get_model();
546                 Job *j;
547
548                 model.get_iter(out iter, p);
549                 model.get(iter, 4, out j);
550
551                 return j;
552         }
553
554         public void job_changed() {
555                 Job j = get_current_job();
556
557                 if (j == null)
558                         clear_job();
559                 else
560                         show_job(j);
561         }
562
563         public void clear_job() {
564                 current_job_id = 0;
565
566                 job_id_label.set_text_or_na();
567                 job_state_label.set_text_or_na();
568                 job_type_label.set_text_or_na();
569
570                 cancel_button.set_sensitive(false);
571         }
572
573         public void show_job(Job job) {
574                 current_job_id = job.id;
575
576                 job_id_label.set_text_or_na("%u".printf(current_job_id));
577                 job_state_label.set_text_or_na(job.state);
578                 job_type_label.set_text_or_na(job.job_type);
579
580                 cancel_button.set_sensitive(true);
581         }
582
583         public void on_start() {
584                 Unit u = get_current_unit();
585
586                 if (u == null)
587                         return;
588
589                 try {
590                         u.start("replace");
591                 } catch (DBus.Error e) {
592                         show_error(e.message);
593                 }
594         }
595
596         public void on_stop() {
597                 Unit u = get_current_unit();
598
599                 if (u == null)
600                         return;
601
602                 try {
603                         u.stop("replace");
604                 } catch (DBus.Error e) {
605                         show_error(e.message);
606                 }
607         }
608
609         public void on_reload() {
610                 Unit u = get_current_unit();
611
612                 if (u == null)
613                         return;
614
615                 try {
616                         u.reload("replace");
617                 } catch (DBus.Error e) {
618                         show_error(e.message);
619                 }
620         }
621
622         public void on_restart() {
623                 Unit u = get_current_unit();
624
625                 if (u == null)
626                         return;
627
628                 try {
629                         u.restart("replace");
630                 } catch (DBus.Error e) {
631                         show_error(e.message);
632                 }
633         }
634
635         public void on_cancel() {
636                 Job j = get_current_job();
637
638                 if (j == null)
639                         return;
640
641                 try {
642                         j.cancel();
643                 } catch (DBus.Error e) {
644                         show_error(e.message);
645                 }
646         }
647
648         public void update_unit_iter(TreeIter iter, string id, Unit u) {
649
650                 string t = "";
651                 Unit.JobLink jl = u.job;
652
653                 if (jl.id != 0) {
654                         Job j = bus.get_object(
655                                         "org.freedesktop.systemd1",
656                                         jl.path,
657                                         "org.freedesktop.systemd1.Job") as Job;
658
659                         t = j.job_type;
660                 }
661
662                 unit_model.set(iter,
663                                0, id,
664                                1, u.description,
665                                2, u.load_state,
666                                3, u.active_state,
667                                4, u.sub_state,
668                                5, t != "" ? "→ %s".printf(t) : "",
669                                6, u);
670         }
671
672         public void on_unit_new(string id, ObjectPath path) {
673                 Unit u = bus.get_object(
674                                 "org.freedesktop.systemd1",
675                                 path,
676                                 "org.freedesktop.systemd1.Unit") as Unit;
677
678                 u.changed += on_unit_changed;
679
680                 TreeIter iter;
681                 unit_model.append(out iter);
682                 update_unit_iter(iter, id, u);
683         }
684
685         public void update_job_iter(TreeIter iter, uint32 id, Job j) {
686                 job_model.set(iter,
687                               0, "%u".printf(id),
688                               1, j.unit.id,
689                               2, "→ %s".printf(j.job_type),
690                               3, j.state,
691                               4, j,
692                               5, id);
693         }
694
695         public void on_job_new(uint32 id, ObjectPath path) {
696                 Job j = bus.get_object(
697                                 "org.freedesktop.systemd1",
698                                 path,
699                                 "org.freedesktop.systemd1.Job") as Job;
700
701                 j.changed += on_job_changed;
702
703                 TreeIter iter;
704                 job_model.append(out iter);
705                 update_job_iter(iter, id, j);
706         }
707
708         public void on_unit_removed(string id, ObjectPath path) {
709                 TreeIter iter;
710                 if (!(unit_model.get_iter_first(out iter)))
711                         return;
712
713                 do {
714                         string name;
715
716                         unit_model.get(iter, 0, out name);
717
718                         if (id == name) {
719                                 if (current_unit_id == name)
720                                         clear_unit();
721
722                                 unit_model.remove(iter);
723                                 break;
724                         }
725
726                 } while (unit_model.iter_next(ref iter));
727         }
728
729         public void on_job_removed(uint32 id, ObjectPath path) {
730                 TreeIter iter;
731                 if (!(job_model.get_iter_first(out iter)))
732                         return;
733
734                 do {
735                         uint32 j;
736
737                         job_model.get(iter, 5, out j);
738
739                         if (id == j) {
740                                 if (current_job_id == j)
741                                         clear_job();
742
743                                 job_model.remove(iter);
744
745                                 break;
746                         }
747
748                 } while (job_model.iter_next(ref iter));
749         }
750
751         public void on_unit_changed(Unit u) {
752                 TreeIter iter;
753                 string id;
754
755                 if (!(unit_model.get_iter_first(out iter)))
756                         return;
757
758                 id = u.id;
759
760                 do {
761                         string name;
762
763                         unit_model.get(iter, 0, out name);
764
765                         if (id == name) {
766                                 update_unit_iter(iter, id, u);
767
768                                 if (current_unit_id == id)
769                                         show_unit(u);
770
771                                 break;
772                         }
773
774                 } while (unit_model.iter_next(ref iter));
775         }
776
777         public void on_job_changed(Job j) {
778                 TreeIter iter;
779                 uint32 id;
780
781                 if (!(job_model.get_iter_first(out iter)))
782                         return;
783
784                 id = j.id;
785
786                 do {
787                         uint32 k;
788
789                         job_model.get(iter, 5, out k);
790
791                         if (id == k) {
792                                 update_job_iter(iter, id, j);
793
794                                 if (current_job_id == id)
795                                         show_job(j);
796
797                                 break;
798                         }
799
800                 } while (job_model.iter_next(ref iter));
801         }
802
803         public bool unit_filter(TreeModel model, TreeIter iter) {
804                 string id, active_state, job;
805
806                 model.get(iter, 0, out id, 3, out active_state, 5, out job);
807
808                 if (id == null)
809                         return false;
810
811                 switch (unit_type_combo_box.get_active()) {
812
813                         case 0:
814                                 return true;
815
816                         case 1:
817                                 return active_state != "inactive" || job != "";
818
819                         case 2:
820                                 return id.has_suffix(".service");
821
822                         case 3:
823                                 return id.has_suffix(".socket");
824
825                         case 4:
826                                 return id.has_suffix(".device");
827
828                         case 5:
829                                 return id.has_suffix(".mount");
830
831                         case 6:
832                                 return id.has_suffix(".automount");
833
834                         case 7:
835                                 return id.has_suffix(".target");
836
837                         case 8:
838                                 return id.has_suffix(".snapshot");
839                 }
840
841                 return false;
842         }
843
844         public void unit_type_changed() {
845                 TreeModelFilter model = (TreeModelFilter) unit_view.get_model();
846
847                 model.refilter();
848         }
849
850         public void on_server_reload() {
851                 try {
852                         manager.reload();
853                 } catch (DBus.Error e) {
854                         show_error(e.message);
855                 }
856         }
857
858         public void on_server_snapshot() {
859                 try {
860                         manager.create_snapshot();
861
862                         if (unit_type_combo_box.get_active() != 0)
863                                 unit_type_combo_box.set_active(8);
864
865                 } catch (DBus.Error e) {
866                         show_error(e.message);
867                 }
868         }
869
870         public void on_unit_load() {
871                 string t = unit_load_entry.get_text();
872
873                 if (t == "")
874                         return;
875
876                 try {
877                         var path = manager.load_unit(t);
878
879                         Unit u = bus.get_object(
880                                         "org.freedesktop.systemd1",
881                                         path,
882                                         "org.freedesktop.systemd1.Unit") as Unit;
883
884                         var m = new MessageDialog(this,
885                                                   DialogFlags.DESTROY_WITH_PARENT,
886                                                   MessageType.INFO,
887                                                   ButtonsType.CLOSE,
888                                                   "Unit available as id %s", u.id);
889                         m.title = "Unit";
890                         m.run();
891                         m.destroy();
892
893                         show_unit(u);
894                 } catch (DBus.Error e) {
895                         show_error(e.message);
896                 }
897         }
898
899         public void on_unit_load_entry_changed() {
900                 unit_load_button.set_sensitive(unit_load_entry.get_text() != "");
901         }
902
903         public bool on_activate_link(string uri) {
904
905                 try {
906                         string path = manager.get_unit(uri);
907
908                         Unit u = bus.get_object(
909                                         "org.freedesktop.systemd1",
910                                         path,
911                                         "org.freedesktop.systemd1.Unit") as Unit;
912
913                         show_unit(u);
914                 } catch (DBus.Error e) {
915                         show_error(e.message);
916                 }
917
918                 return true;
919         }
920
921         public void show_error(string e) {
922                 var m = new MessageDialog(this,
923                                           DialogFlags.DESTROY_WITH_PARENT,
924                                           MessageType.ERROR,
925                                           ButtonsType.CLOSE, "%s", e);
926                 m.title = "Error";
927                 m.run();
928                 m.destroy();
929         }
930
931 }
932
933 static const OptionEntry entries[] = {
934         { "session", 0,   0,                   OptionArg.NONE,   out session, "Connect to session bus", null },
935         { "system",  0,   OptionFlags.REVERSE, OptionArg.NONE,   out session, "Connect to system bus", null },
936         { null }
937 };
938
939 void show_error(string e) {
940         var m = new MessageDialog(null, 0, MessageType.ERROR, ButtonsType.CLOSE, "%s", e);
941         m.run();
942         m.destroy();
943 }
944
945 int main (string[] args) {
946
947         try {
948                 Gtk.init_with_args(ref args, "[OPTION...]", entries, "systemadm");
949
950                 MainWindow window = new MainWindow();
951                 window.show_all();
952
953                 Gtk.main();
954         } catch (DBus.Error e) {
955                 show_error(e.message);
956         } catch (GLib.Error e) {
957                 show_error(e.message);
958         }
959
960         return 0;
961 }