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