chiark / gitweb /
util: properly divide in 64bit in format_timestamp()
[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
506                 string fp = unit.fragment_path;
507                 if (fp != "")
508                         unit_fragment_path_label.set_markup_or_na("<a href=\"file://" + fp +"\">" + fp + "</a>" );
509                 else
510                         unit_fragment_path_label.set_text_or_na();
511
512                 uint64 t = unit.active_enter_timestamp;
513                 if (t > 0) {
514                         Time timestamp = Time.local((time_t) (t / 1000000));
515                         unit_active_enter_timestamp_label.set_text_or_na(timestamp.format("%a, %d %b %Y %H:%M:%S %z"));
516                 } else
517                         unit_active_enter_timestamp_label.set_text_or_na();
518
519                 t = unit.active_exit_timestamp;
520                 if (t > 0) {
521                         Time timestamp = Time.local((time_t) (t / 1000000));
522                         unit_active_exit_timestamp_label.set_text_or_na(timestamp.format("%a, %d %b %Y %H:%M:%S %z"));
523                 } else
524                         unit_active_exit_timestamp_label.set_text_or_na();
525
526                 bool b = unit.can_start;
527                 start_button.set_sensitive(b);
528                 stop_button.set_sensitive(b);
529                 restart_button.set_sensitive(b);
530                 unit_can_start_label.set_text_or_na(b ? "Yes" : "No");
531
532                 b = unit.can_reload;
533                 reload_button.set_sensitive(b);
534                 unit_can_reload_label.set_text_or_na(b ? "Yes" : "No");
535
536                 unit_cgroup_label.set_text_or_na(unit.default_control_group);
537         }
538
539         public Job? get_current_job() {
540                 TreePath p;
541                 job_view.get_cursor(out p, null);
542
543                 if (p == null)
544                         return null;
545
546                 TreeIter iter;
547                 TreeModel model = job_view.get_model();
548                 Job *j;
549
550                 model.get_iter(out iter, p);
551                 model.get(iter, 4, out j);
552
553                 return j;
554         }
555
556         public void job_changed() {
557                 Job j = get_current_job();
558
559                 if (j == null)
560                         clear_job();
561                 else
562                         show_job(j);
563         }
564
565         public void clear_job() {
566                 current_job_id = 0;
567
568                 job_id_label.set_text_or_na();
569                 job_state_label.set_text_or_na();
570                 job_type_label.set_text_or_na();
571
572                 cancel_button.set_sensitive(false);
573         }
574
575         public void show_job(Job job) {
576                 current_job_id = job.id;
577
578                 job_id_label.set_text_or_na("%u".printf(current_job_id));
579                 job_state_label.set_text_or_na(job.state);
580                 job_type_label.set_text_or_na(job.job_type);
581
582                 cancel_button.set_sensitive(true);
583         }
584
585         public void on_start() {
586                 Unit u = get_current_unit();
587
588                 if (u == null)
589                         return;
590
591                 try {
592                         u.start("replace");
593                 } catch (DBus.Error e) {
594                         show_error(e.message);
595                 }
596         }
597
598         public void on_stop() {
599                 Unit u = get_current_unit();
600
601                 if (u == null)
602                         return;
603
604                 try {
605                         u.stop("replace");
606                 } catch (DBus.Error e) {
607                         show_error(e.message);
608                 }
609         }
610
611         public void on_reload() {
612                 Unit u = get_current_unit();
613
614                 if (u == null)
615                         return;
616
617                 try {
618                         u.reload("replace");
619                 } catch (DBus.Error e) {
620                         show_error(e.message);
621                 }
622         }
623
624         public void on_restart() {
625                 Unit u = get_current_unit();
626
627                 if (u == null)
628                         return;
629
630                 try {
631                         u.restart("replace");
632                 } catch (DBus.Error e) {
633                         show_error(e.message);
634                 }
635         }
636
637         public void on_cancel() {
638                 Job j = get_current_job();
639
640                 if (j == null)
641                         return;
642
643                 try {
644                         j.cancel();
645                 } catch (DBus.Error e) {
646                         show_error(e.message);
647                 }
648         }
649
650         public void update_unit_iter(TreeIter iter, string id, Unit u) {
651
652                 string t = "";
653                 Unit.JobLink jl = u.job;
654
655                 if (jl.id != 0) {
656                         Job j = bus.get_object(
657                                         "org.freedesktop.systemd1",
658                                         jl.path,
659                                         "org.freedesktop.systemd1.Job") as Job;
660
661                         t = j.job_type;
662                 }
663
664                 unit_model.set(iter,
665                                0, id,
666                                1, u.description,
667                                2, u.load_state,
668                                3, u.active_state,
669                                4, u.sub_state,
670                                5, t != "" ? "→ %s".printf(t) : "",
671                                6, u);
672         }
673
674         public void on_unit_new(string id, ObjectPath path) {
675                 Unit u = bus.get_object(
676                                 "org.freedesktop.systemd1",
677                                 path,
678                                 "org.freedesktop.systemd1.Unit") as Unit;
679
680                 u.changed += on_unit_changed;
681
682                 TreeIter iter;
683                 unit_model.append(out iter);
684                 update_unit_iter(iter, id, u);
685         }
686
687         public void update_job_iter(TreeIter iter, uint32 id, Job j) {
688                 job_model.set(iter,
689                               0, "%u".printf(id),
690                               1, j.unit.id,
691                               2, "→ %s".printf(j.job_type),
692                               3, j.state,
693                               4, j,
694                               5, id);
695         }
696
697         public void on_job_new(uint32 id, ObjectPath path) {
698                 Job j = bus.get_object(
699                                 "org.freedesktop.systemd1",
700                                 path,
701                                 "org.freedesktop.systemd1.Job") as Job;
702
703                 j.changed += on_job_changed;
704
705                 TreeIter iter;
706                 job_model.append(out iter);
707                 update_job_iter(iter, id, j);
708         }
709
710         public void on_unit_removed(string id, ObjectPath path) {
711                 TreeIter iter;
712                 if (!(unit_model.get_iter_first(out iter)))
713                         return;
714
715                 do {
716                         string name;
717
718                         unit_model.get(iter, 0, out name);
719
720                         if (id == name) {
721                                 if (current_unit_id == name)
722                                         clear_unit();
723
724                                 unit_model.remove(iter);
725                                 break;
726                         }
727
728                 } while (unit_model.iter_next(ref iter));
729         }
730
731         public void on_job_removed(uint32 id, ObjectPath path, bool success) {
732                 TreeIter iter;
733                 if (!(job_model.get_iter_first(out iter)))
734                         return;
735
736                 do {
737                         uint32 j;
738
739                         job_model.get(iter, 5, out j);
740
741                         if (id == j) {
742                                 if (current_job_id == j)
743                                         clear_job();
744
745                                 job_model.remove(iter);
746
747                                 break;
748                         }
749
750                 } while (job_model.iter_next(ref iter));
751         }
752
753         public void on_unit_changed(Unit u) {
754                 TreeIter iter;
755                 string id;
756
757                 if (!(unit_model.get_iter_first(out iter)))
758                         return;
759
760                 id = u.id;
761
762                 do {
763                         string name;
764
765                         unit_model.get(iter, 0, out name);
766
767                         if (id == name) {
768                                 update_unit_iter(iter, id, u);
769
770                                 if (current_unit_id == id)
771                                         show_unit(u);
772
773                                 break;
774                         }
775
776                 } while (unit_model.iter_next(ref iter));
777         }
778
779         public void on_job_changed(Job j) {
780                 TreeIter iter;
781                 uint32 id;
782
783                 if (!(job_model.get_iter_first(out iter)))
784                         return;
785
786                 id = j.id;
787
788                 do {
789                         uint32 k;
790
791                         job_model.get(iter, 5, out k);
792
793                         if (id == k) {
794                                 update_job_iter(iter, id, j);
795
796                                 if (current_job_id == id)
797                                         show_job(j);
798
799                                 break;
800                         }
801
802                 } while (job_model.iter_next(ref iter));
803         }
804
805         public bool unit_filter(TreeModel model, TreeIter iter) {
806                 string id, active_state, job;
807
808                 model.get(iter, 0, out id, 3, out active_state, 5, out job);
809
810                 if (id == null)
811                         return false;
812
813                 switch (unit_type_combo_box.get_active()) {
814
815                         case 0:
816                                 return true;
817
818                         case 1:
819                                 return active_state != "inactive" || job != "";
820
821                         case 2:
822                                 return id.has_suffix(".service");
823
824                         case 3:
825                                 return id.has_suffix(".socket");
826
827                         case 4:
828                                 return id.has_suffix(".device");
829
830                         case 5:
831                                 return id.has_suffix(".mount");
832
833                         case 6:
834                                 return id.has_suffix(".automount");
835
836                         case 7:
837                                 return id.has_suffix(".target");
838
839                         case 8:
840                                 return id.has_suffix(".snapshot");
841                 }
842
843                 return false;
844         }
845
846         public void unit_type_changed() {
847                 TreeModelFilter model = (TreeModelFilter) unit_view.get_model();
848
849                 model.refilter();
850         }
851
852         public void on_server_reload() {
853                 try {
854                         manager.reload();
855                 } catch (DBus.Error e) {
856                         show_error(e.message);
857                 }
858         }
859
860         public void on_server_snapshot() {
861                 try {
862                         manager.create_snapshot();
863
864                         if (unit_type_combo_box.get_active() != 0)
865                                 unit_type_combo_box.set_active(8);
866
867                 } catch (DBus.Error e) {
868                         show_error(e.message);
869                 }
870         }
871
872         public void on_unit_load() {
873                 string t = unit_load_entry.get_text();
874
875                 if (t == "")
876                         return;
877
878                 try {
879                         var path = manager.load_unit(t);
880
881                         Unit u = bus.get_object(
882                                         "org.freedesktop.systemd1",
883                                         path,
884                                         "org.freedesktop.systemd1.Unit") as Unit;
885
886                         var m = new MessageDialog(this,
887                                                   DialogFlags.DESTROY_WITH_PARENT,
888                                                   MessageType.INFO,
889                                                   ButtonsType.CLOSE,
890                                                   "Unit available as id %s", u.id);
891                         m.title = "Unit";
892                         m.run();
893                         m.destroy();
894
895                         show_unit(u);
896                 } catch (DBus.Error e) {
897                         show_error(e.message);
898                 }
899         }
900
901         public void on_unit_load_entry_changed() {
902                 unit_load_button.set_sensitive(unit_load_entry.get_text() != "");
903         }
904
905         public bool on_activate_link(string uri) {
906
907                 try {
908                         string path = manager.get_unit(uri);
909
910                         Unit u = bus.get_object(
911                                         "org.freedesktop.systemd1",
912                                         path,
913                                         "org.freedesktop.systemd1.Unit") as Unit;
914
915                         show_unit(u);
916                 } catch (DBus.Error e) {
917                         show_error(e.message);
918                 }
919
920                 return true;
921         }
922
923         public void show_error(string e) {
924                 var m = new MessageDialog(this,
925                                           DialogFlags.DESTROY_WITH_PARENT,
926                                           MessageType.ERROR,
927                                           ButtonsType.CLOSE, "%s", e);
928                 m.title = "Error";
929                 m.run();
930                 m.destroy();
931         }
932
933 }
934
935 static const OptionEntry entries[] = {
936         { "session", 0,   0,                   OptionArg.NONE,   out session, "Connect to session bus", null },
937         { "system",  0,   OptionFlags.REVERSE, OptionArg.NONE,   out session, "Connect to system bus", null },
938         { null }
939 };
940
941 void show_error(string e) {
942         var m = new MessageDialog(null, 0, MessageType.ERROR, ButtonsType.CLOSE, "%s", e);
943         m.run();
944         m.destroy();
945 }
946
947 int main (string[] args) {
948
949         try {
950                 Gtk.init_with_args(ref args, "[OPTION...]", entries, "systemadm");
951
952                 MainWindow window = new MainWindow();
953                 window.show_all();
954
955                 Gtk.main();
956         } catch (DBus.Error e) {
957                 show_error(e.message);
958         } catch (GLib.Error e) {
959                 show_error(e.message);
960         }
961
962         return 0;
963 }