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