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