chiark / gitweb /
systemadm: show dependencies in the UI
[elogind.git] / systemadm.vala
index 423528aa3f552a5b37bde2aef14f6479c1c80eb7..de2fa3ae9c6d1fae988520f5c9bd3cc95c6aef0d 100644 (file)
@@ -47,6 +47,13 @@ public class RightLabel : Label {
                 else
                         set_text(text);
         }
+
+        public void set_markup_or_na(string? text = null) {
+                if (text == null || text == "")
+                        set_markup("<i>n/a</i>");
+                else
+                        set_markup(text);
+        }
 }
 
 public class MainWindow : Window {
@@ -66,6 +73,9 @@ public class MainWindow : Window {
         private Button reload_button;
         private Button cancel_button;
 
+        private Entry unit_load_entry;
+        private Button unit_load_button;
+
         private Button server_snapshot_button;
         private Button server_reload_button;
 
@@ -74,6 +84,7 @@ public class MainWindow : Window {
 
         private RightLabel unit_id_label;
         private RightLabel unit_aliases_label;
+        private RightLabel unit_dependency_label;
         private RightLabel unit_description_label;
         private RightLabel unit_load_state_label;
         private RightLabel unit_active_state_label;
@@ -92,7 +103,7 @@ public class MainWindow : Window {
         private ComboBox unit_type_combo_box;
 
         public MainWindow() throws DBus.Error {
-                title = "systemdadm";
+                title = "systemadm";
                 position = WindowPosition.CENTER;
                 set_default_size(1000, 700);
                 set_border_width(12);
@@ -126,7 +137,19 @@ public class MainWindow : Window {
                 unit_type_combo_box.set_active(1);
                 unit_type_combo_box.changed += unit_type_changed;
 
-                server_snapshot_button = new Button.with_mnemonic("Take _Snapshot");
+                unit_load_entry = new Entry();
+                unit_load_button = new Button.with_mnemonic("_Load");
+                unit_load_button.set_sensitive(false);
+
+                unit_load_entry.changed += on_unit_load_entry_changed;
+                unit_load_entry.activate += on_unit_load;
+                unit_load_button.clicked += on_unit_load;
+
+                Box unit_load_hbox = new HBox(false, 6);
+                unit_load_hbox.pack_start(unit_load_entry, false, true, 0);
+                unit_load_hbox.pack_start(unit_load_button, false, true, 0);
+
+                server_snapshot_button = new Button.with_mnemonic("Take S_napshot");
                 server_reload_button = new Button.with_mnemonic("Reload _Configuration");
 
                 server_snapshot_button.clicked += on_server_snapshot;
@@ -134,6 +157,7 @@ public class MainWindow : Window {
 
                 type_hbox.pack_end(server_snapshot_button, false, true, 0);
                 type_hbox.pack_end(server_reload_button, false, true, 0);
+                type_hbox.pack_end(unit_load_hbox, false, true, 24);
 
                 unit_model = new ListStore(7, typeof(string), typeof(string), typeof(string), typeof(string), typeof(string), typeof(string), typeof(Unit));
                 job_model = new ListStore(6, typeof(string), typeof(string), typeof(string), typeof(string), typeof(Job), typeof(uint32));
@@ -173,6 +197,7 @@ public class MainWindow : Window {
 
                 unit_id_label = new RightLabel();
                 unit_aliases_label = new RightLabel();
+                unit_dependency_label = new RightLabel();
                 unit_description_label = new RightLabel();
                 unit_load_state_label = new RightLabel();
                 unit_active_state_label = new RightLabel();
@@ -188,6 +213,10 @@ public class MainWindow : Window {
                 job_state_label = new RightLabel();
                 job_type_label = new RightLabel();
 
+                unit_dependency_label.set_track_visited_links(false);
+                unit_dependency_label.set_selectable(false);
+                unit_dependency_label.activate_link += on_activate_link;
+
                 Table unit_table = new Table(8, 6, false);
                 unit_table.set_row_spacings(6);
                 unit_table.set_border_width(0);
@@ -204,27 +233,29 @@ public class MainWindow : Window {
                 unit_table.attach(unit_aliases_label,                       1, 6, 1, 2, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
                 unit_table.attach(new LeftLabel("Description:"),            0, 1, 2, 3, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
                 unit_table.attach(unit_description_label,                   1, 6, 2, 3, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
-                unit_table.attach(new LeftLabel("Fragment Path:"),          0, 1, 3, 4, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
-                unit_table.attach(unit_fragment_path_label,                 1, 6, 3, 4, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
-                unit_table.attach(new LeftLabel("Control Group:"),          0, 1, 4, 5, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
-                unit_table.attach(unit_cgroup_label,                        1, 6, 4, 5, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
-
-                unit_table.attach(new LeftLabel("Load State:"),             0, 1, 5, 6, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
-                unit_table.attach(unit_load_state_label,                    1, 2, 5, 6, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
-                unit_table.attach(new LeftLabel("Active State:"),           0, 1, 6, 7, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
-                unit_table.attach(unit_active_state_label,                  1, 2, 6, 7, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
-                unit_table.attach(new LeftLabel("Unit State:"),             0, 1, 7, 8, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
-                unit_table.attach(unit_sub_state_label,                     1, 2, 7, 8, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
-
-                unit_table.attach(new LeftLabel("Active Enter Timestamp:"), 2, 3, 6, 7, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
-                unit_table.attach(unit_active_enter_timestamp_label,        3, 4, 6, 7, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
-                unit_table.attach(new LeftLabel("Active Exit Timestamp:"),  2, 3, 7, 8, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
-                unit_table.attach(unit_active_exit_timestamp_label,         3, 4, 7, 8, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
-
-                unit_table.attach(new LeftLabel("Can Start/Stop:"),         4, 5, 6, 7, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
-                unit_table.attach(unit_can_start_label,                     5, 6, 6, 7, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
-                unit_table.attach(new LeftLabel("Can Reload:"),             4, 5, 7, 8, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
-                unit_table.attach(unit_can_reload_label,                    5, 6, 7, 8, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
+                unit_table.attach(new LeftLabel("Dependencies:"),           0, 1, 3, 4, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
+                unit_table.attach(unit_dependency_label,                    1, 6, 3, 4, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
+                unit_table.attach(new LeftLabel("Fragment Path:"),          0, 1, 4, 5, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
+                unit_table.attach(unit_fragment_path_label,                 1, 6, 4, 5, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
+                unit_table.attach(new LeftLabel("Control Group:"),          0, 1, 5, 6, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
+                unit_table.attach(unit_cgroup_label,                        1, 6, 5, 6, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
+
+                unit_table.attach(new LeftLabel("Load State:"),             0, 1, 6, 7, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
+                unit_table.attach(unit_load_state_label,                    1, 2, 6, 7, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
+                unit_table.attach(new LeftLabel("Active State:"),           0, 1, 7, 8, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
+                unit_table.attach(unit_active_state_label,                  1, 2, 7, 8, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
+                unit_table.attach(new LeftLabel("Unit State:"),             0, 1, 8, 9, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
+                unit_table.attach(unit_sub_state_label,                     1, 2, 8, 9, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
+
+                unit_table.attach(new LeftLabel("Active Enter Timestamp:"), 2, 3, 7, 8, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
+                unit_table.attach(unit_active_enter_timestamp_label,        3, 4, 7, 8, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
+                unit_table.attach(new LeftLabel("Active Exit Timestamp:"),  2, 3, 8, 9, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
+                unit_table.attach(unit_active_exit_timestamp_label,         3, 4, 8, 9, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
+
+                unit_table.attach(new LeftLabel("Can Start/Stop:"),         4, 5, 7, 8, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
+                unit_table.attach(unit_can_start_label,                     5, 6, 7, 8, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
+                unit_table.attach(new LeftLabel("Can Reload:"),             4, 5, 8, 9, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
+                unit_table.attach(unit_can_reload_label,                    5, 6, 8, 9, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
 
                 job_table.attach(new LeftLabel("Id:"),                      0, 1, 0, 1, AttachOptions.FILL, AttachOptions.FILL, 0, 0);
                 job_table.attach(job_id_label,                              1, 2, 0, 1, AttachOptions.EXPAND|AttachOptions.FILL, AttachOptions.FILL, 0, 0);
@@ -297,6 +328,8 @@ public class MainWindow : Window {
                                         i.unit_path,
                                         "org.freedesktop.systemd1.Unit") as Unit;
 
+                        u.changed += on_unit_changed;
+
                         unit_model.append(out iter);
                         unit_model.set(iter,
                                        0, i.id,
@@ -322,6 +355,8 @@ public class MainWindow : Window {
                                         i.job_path,
                                         "org.freedesktop.systemd1.Job") as Job;
 
+                        j.changed += on_job_changed;
+
                         job_model.append(out iter);
                         job_model.set(iter,
                                       0, "%u".printf(i.id),
@@ -370,6 +405,7 @@ public class MainWindow : Window {
                 unit_id_label.set_text_or_na();
                 unit_aliases_label.set_text_or_na();
                 unit_description_label.set_text_or_na();
+                unit_description_label.set_text_or_na();
                 unit_load_state_label.set_text_or_na();
                 unit_active_state_label.set_text_or_na();
                 unit_sub_state_label.set_text_or_na();
@@ -381,11 +417,84 @@ public class MainWindow : Window {
                 unit_cgroup_label.set_text_or_na();
         }
 
+        public string make_dependency_string(string? prefix, string word, string[] dependencies) {
+                bool first = true;
+                string r;
+
+                if (prefix == null)
+                        r = "";
+                else
+                        r = prefix;
+
+                foreach (string i in dependencies) {
+                        if (r != "")
+                                r += first ? "\n" : ",";
+
+                        if (first) {
+                                r += word;
+                                first = false;
+                        }
+
+                        r += " <a href=\"" + i + "\">" + i + "</a>";
+                }
+
+                return r;
+        }
+
         public void show_unit(Unit unit) {
                 current_unit_id = unit.id;
 
                 unit_id_label.set_text_or_na(current_unit_id);
-                unit_aliases_label.set_text_or_na(string.joinv("\n", unit.names));
+
+                string a = "";
+                foreach (string i in unit.names) {
+                        if (i == current_unit_id)
+                                continue;
+
+                        if (a == "")
+                                a = i;
+                        else
+                                a += "\n" + i;
+                }
+
+                unit_aliases_label.set_text_or_na(a);
+
+                string[]
+                        requires = unit.requires,
+                        requires_overridable = unit.requires_overridable,
+                        requisite = unit.requisite,
+                        requisite_overridable = unit.requisite_overridable,
+                        wants = unit.wants,
+                        required_by = unit.required_by,
+                        required_by_overridable = unit.required_by_overridable,
+                        wanted_by = unit.wanted_by,
+                        conflicts = unit.conflicts,
+                        before = unit.before,
+                        after = unit.after;
+
+                unit_dependency_label.set_markup_or_na(
+                                make_dependency_string(
+                                make_dependency_string(
+                                make_dependency_string(
+                                make_dependency_string(
+                                make_dependency_string(
+                                make_dependency_string(
+                                make_dependency_string(
+                                make_dependency_string(
+                                make_dependency_string(
+                                make_dependency_string(
+                                make_dependency_string(null,
+                                "requires", requires),
+                                "overridable requires", requires_overridable),
+                                "requisite", requisite),
+                                "overridable requisite", requisite_overridable),
+                                "wants", wants),
+                                "conflicts", conflicts),
+                                "required by", required_by),
+                                "overridable required by", required_by_overridable),
+                                "wanted by", wanted_by),
+                                "after", after),
+                                "before", before));
 
                 unit_description_label.set_text_or_na(unit.description);
                 unit_load_state_label.set_text_or_na(unit.load_state);
@@ -395,15 +504,15 @@ public class MainWindow : Window {
 
                 uint64 t = unit.active_enter_timestamp;
                 if (t > 0) {
-                        TimeVal tv = { (long) (t / 1000000), (long) (t % 1000000) };
-                        unit_active_enter_timestamp_label.set_text_or_na(tv.to_iso8601());
+                        Time timestamp = Time.local((time_t) (t / 1000000));
+                        unit_active_enter_timestamp_label.set_text_or_na(timestamp.format("%a, %d %b %Y %H:%M:%S %z"));
                 } else
                         unit_active_enter_timestamp_label.set_text_or_na();
 
                 t = unit.active_exit_timestamp;
                 if (t > 0) {
-                        TimeVal tv = { (long) (t / 1000000), (long) (t % 1000000) };
-                        unit_active_exit_timestamp_label.set_text_or_na(tv.to_iso8601());
+                        Time timestamp = Time.local((time_t) (t / 1000000));
+                        unit_active_exit_timestamp_label.set_text_or_na(timestamp.format("%a, %d %b %Y %H:%M:%S %z"));
                 } else
                         unit_active_exit_timestamp_label.set_text_or_na();
 
@@ -475,7 +584,7 @@ public class MainWindow : Window {
                 try {
                         u.start("replace");
                 } catch (DBus.Error e) {
-                        message("%s", e.message);
+                        show_error(e.message);
                 }
         }
 
@@ -488,7 +597,7 @@ public class MainWindow : Window {
                 try {
                         u.stop("replace");
                 } catch (DBus.Error e) {
-                        message("%s", e.message);
+                        show_error(e.message);
                 }
         }
 
@@ -501,7 +610,7 @@ public class MainWindow : Window {
                 try {
                         u.reload("replace");
                 } catch (DBus.Error e) {
-                        message("%s", e.message);
+                        show_error(e.message);
                 }
         }
 
@@ -514,7 +623,7 @@ public class MainWindow : Window {
                 try {
                         u.restart("replace");
                 } catch (DBus.Error e) {
-                        message("%s", e.message);
+                        show_error(e.message);
                 }
         }
 
@@ -527,15 +636,11 @@ public class MainWindow : Window {
                 try {
                         j.cancel();
                 } catch (DBus.Error e) {
-                        message("%s", e.message);
+                        show_error(e.message);
                 }
         }
 
-        public void on_unit_new(string id, ObjectPath path) {
-                Unit u = bus.get_object(
-                                "org.freedesktop.systemd1",
-                                path,
-                                "org.freedesktop.systemd1.Unit") as Unit;
+        public void update_unit_iter(TreeIter iter, string id, Unit u) {
 
                 string t = "";
                 Unit.JobLink jl = u.job;
@@ -549,8 +654,6 @@ public class MainWindow : Window {
                         t = j.job_type;
                 }
 
-                TreeIter iter;
-                unit_model.append(out iter);
                 unit_model.set(iter,
                                0, id,
                                1, u.description,
@@ -561,16 +664,22 @@ public class MainWindow : Window {
                                6, u);
         }
 
-        public void on_job_new(uint32 id, ObjectPath path) {
-                Job j = bus.get_object(
+        public void on_unit_new(string id, ObjectPath path) {
+                Unit u = bus.get_object(
                                 "org.freedesktop.systemd1",
                                 path,
-                                "org.freedesktop.systemd1.Job") as Job;
+                                "org.freedesktop.systemd1.Unit") as Unit;
+
+                u.changed += on_unit_changed;
 
                 TreeIter iter;
-                job_model.append(out iter);
+                unit_model.append(out iter);
+                update_unit_iter(iter, id, u);
+        }
+
+        public void update_job_iter(TreeIter iter, uint32 id, Job j) {
                 job_model.set(iter,
-                              0, "%u".printf(j.id),
+                              0, "%u".printf(id),
                               1, j.unit.id,
                               2, "→ %s".printf(j.job_type),
                               3, j.state,
@@ -578,6 +687,19 @@ public class MainWindow : Window {
                               5, id);
         }
 
+        public void on_job_new(uint32 id, ObjectPath path) {
+                Job j = bus.get_object(
+                                "org.freedesktop.systemd1",
+                                path,
+                                "org.freedesktop.systemd1.Job") as Job;
+
+                j.changed += on_job_changed;
+
+                TreeIter iter;
+                job_model.append(out iter);
+                update_job_iter(iter, id, j);
+        }
+
         public void on_unit_removed(string id, ObjectPath path) {
                 TreeIter iter;
                 if (!(unit_model.get_iter_first(out iter)))
@@ -621,10 +743,62 @@ public class MainWindow : Window {
                 } while (job_model.iter_next(ref iter));
         }
 
+        public void on_unit_changed(Unit u) {
+                TreeIter iter;
+                string id;
+
+                if (!(unit_model.get_iter_first(out iter)))
+                        return;
+
+                id = u.id;
+
+                do {
+                        string name;
+
+                        unit_model.get(iter, 0, out name);
+
+                        if (id == name) {
+                                update_unit_iter(iter, id, u);
+
+                                if (current_unit_id == id)
+                                        show_unit(u);
+
+                                break;
+                        }
+
+                } while (unit_model.iter_next(ref iter));
+        }
+
+        public void on_job_changed(Job j) {
+                TreeIter iter;
+                uint32 id;
+
+                if (!(job_model.get_iter_first(out iter)))
+                        return;
+
+                id = j.id;
+
+                do {
+                        uint32 k;
+
+                        job_model.get(iter, 5, out k);
+
+                        if (id == k) {
+                                update_job_iter(iter, id, j);
+
+                                if (current_job_id == id)
+                                        show_job(j);
+
+                                break;
+                        }
+
+                } while (job_model.iter_next(ref iter));
+        }
+
         public bool unit_filter(TreeModel model, TreeIter iter) {
-                string id, active_state;
+                string id, active_state, job;
 
-                model.get(iter, 0, out id, 3, out active_state);
+                model.get(iter, 0, out id, 3, out active_state, 5, out job);
 
                 if (id == null)
                         return false;
@@ -635,7 +809,7 @@ public class MainWindow : Window {
                                 return true;
 
                         case 1:
-                                return active_state != "inactive";
+                                return active_state != "inactive" || job != "";
 
                         case 2:
                                 return id.has_suffix(".service");
@@ -672,7 +846,7 @@ public class MainWindow : Window {
                 try {
                         manager.reload();
                 } catch (DBus.Error e) {
-                        message("%s", e.message);
+                        show_error(e.message);
                 }
         }
 
@@ -684,9 +858,70 @@ public class MainWindow : Window {
                                 unit_type_combo_box.set_active(8);
 
                 } catch (DBus.Error e) {
-                        message("%s", e.message);
+                        show_error(e.message);
                 }
         }
+
+        public void on_unit_load() {
+                string t = unit_load_entry.get_text();
+
+                if (t == "")
+                        return;
+
+                try {
+                        var path = manager.load_unit(t);
+
+                        Unit u = bus.get_object(
+                                        "org.freedesktop.systemd1",
+                                        path,
+                                        "org.freedesktop.systemd1.Unit") as Unit;
+
+                        var m = new MessageDialog(this,
+                                                  DialogFlags.DESTROY_WITH_PARENT,
+                                                  MessageType.INFO,
+                                                  ButtonsType.CLOSE,
+                                                  "Unit available as id %s", u.id);
+                        m.title = "Unit";
+                        m.run();
+                        m.destroy();
+
+                } catch (DBus.Error e) {
+                        show_error(e.message);
+                }
+        }
+
+        public void on_unit_load_entry_changed() {
+                unit_load_button.set_sensitive(unit_load_entry.get_text() != "");
+        }
+
+        public bool on_activate_link(string uri) {
+
+                try {
+                        string path = manager.get_unit(uri);
+
+                        Unit u = bus.get_object(
+                                        "org.freedesktop.systemd1",
+                                        path,
+                                        "org.freedesktop.systemd1.Unit") as Unit;
+
+                        show_unit(u);
+                } catch (DBus.Error e) {
+                        show_error(e.message);
+                }
+
+                return true;
+        }
+
+        public void show_error(string e) {
+                var m = new MessageDialog(this,
+                                          DialogFlags.DESTROY_WITH_PARENT,
+                                          MessageType.ERROR,
+                                          ButtonsType.CLOSE, "%s", e);
+                m.title = "Error";
+                m.run();
+                m.destroy();
+        }
+
 }
 
 static const OptionEntry entries[] = {
@@ -695,6 +930,12 @@ static const OptionEntry entries[] = {
         { null }
 };
 
+void show_error(string e) {
+        var m = new MessageDialog(null, 0, MessageType.ERROR, ButtonsType.CLOSE, "%s", e);
+        m.run();
+        m.destroy();
+}
+
 int main (string[] args) {
 
         try {
@@ -705,9 +946,9 @@ int main (string[] args) {
 
                 Gtk.main();
         } catch (DBus.Error e) {
-                message("%s", e.message);
+                show_error(e.message);
         } catch (GLib.Error e) {
-                message("%s", e.message);
+                show_error(e.message);
         }
 
         return 0;