+static int graph_one_property(const char *name, const char *prop, DBusMessageIter *iter) {
+
+ static const char * const colors[] = {
+ "Requires", "[color=\"black\"]",
+ "RequiresOverridable", "[color=\"black\"]",
+ "Requisite", "[color=\"darkblue\"]",
+ "RequisiteOverridable", "[color=\"darkblue\"]",
+ "Wants", "[color=\"grey66\"]",
+ "Conflicts", "[color=\"red\"]",
+ "ConflictedBy", "[color=\"red\"]",
+ "After", "[color=\"green\"]"
+ };
+
+ const char *c = NULL;
+ unsigned i;
+
+ assert(name);
+ assert(prop);
+ assert(iter);
+
+ for (i = 0; i < ELEMENTSOF(colors); i += 2)
+ if (streq(colors[i], prop)) {
+ c = colors[i+1];
+ break;
+ }
+
+ if (!c)
+ return 0;
+
+ if (arg_dot != DEP_ALL)
+ if ((arg_dot == DEP_ORDER) != streq(prop, "After"))
+ return 0;
+
+ if (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_ARRAY &&
+ dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING) {
+ DBusMessageIter sub;
+
+ dbus_message_iter_recurse(iter, &sub);
+
+ for (dbus_message_iter_recurse(iter, &sub);
+ dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID;
+ dbus_message_iter_next(&sub)) {
+ const char *s;
+
+ assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING);
+ dbus_message_iter_get_basic(&sub, &s);
+ printf("\t\"%s\"->\"%s\" %s;\n", name, s, c);
+ }
+ }
+
+ return 0;
+}
+
+static int graph_one(DBusConnection *bus, const struct unit_info *u) {
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
+ const char *interface = "org.freedesktop.systemd1.Unit";
+ int r;
+ DBusMessageIter iter, sub, sub2, sub3;
+
+ assert(bus);
+ assert(u);
+
+ r = bus_method_call_with_reply(
+ bus,
+ "org.freedesktop.systemd1",
+ u->unit_path,
+ "org.freedesktop.DBus.Properties",
+ "GetAll",
+ &reply,
+ NULL,
+ DBUS_TYPE_STRING, &interface,
+ DBUS_TYPE_INVALID);
+ if (r < 0)
+ return r;
+
+ if (!dbus_message_iter_init(reply, &iter) ||
+ dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
+ dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
+ log_error("Failed to parse reply.");
+ return -EIO;
+ }
+
+ for (dbus_message_iter_recurse(&iter, &sub);
+ dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID;
+ dbus_message_iter_next(&sub)) {
+ const char *prop;
+
+ assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_DICT_ENTRY);
+ dbus_message_iter_recurse(&sub, &sub2);
+
+ if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &prop, true) < 0 ||
+ dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
+ log_error("Failed to parse reply.");
+ return -EIO;
+ }
+
+ dbus_message_iter_recurse(&sub2, &sub3);
+ r = graph_one_property(u->id, prop, &sub3);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static int dot(DBusConnection *bus) {
+ _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
+ DBusMessageIter iter, sub;
+ int r;
+
+ r = bus_method_call_with_reply(
+ bus,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "ListUnits",
+ &reply,
+ NULL,
+ DBUS_TYPE_INVALID);
+ if (r < 0)
+ return r;
+
+ if (!dbus_message_iter_init(reply, &iter) ||
+ dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
+ dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
+ log_error("Failed to parse reply.");
+ return -EIO;
+ }
+
+ printf("digraph systemd {\n");
+
+ for (dbus_message_iter_recurse(&iter, &sub);
+ dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID;
+ dbus_message_iter_next(&sub)) {
+ struct unit_info u;
+
+ r = bus_parse_unit_info(&sub, &u);
+ if (r < 0)
+ return -EIO;
+
+ r = graph_one(bus, &u);
+ if (r < 0)
+ return r;
+ }
+
+ printf("}\n");
+
+ log_info(" Color legend: black = Requires\n"
+ " dark blue = Requisite\n"
+ " dark grey = Wants\n"
+ " red = Conflicts\n"
+ " green = After\n");
+
+ if (on_tty())
+ log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
+ "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n");
+
+ return 0;
+}
+