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