diff options
author | Lennart Poettering <lennart@poettering.net> | 2012-05-21 15:12:18 +0200 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2012-05-21 15:14:51 +0200 |
commit | 49dbfa7b2b0bf3906704dac1eaeb4eba91056a19 (patch) | |
tree | b7bf6ad5bef741f68b45a431807f58a81a5c845a /src | |
parent | a223b325b409b325f4c2c2e11268596edd842631 (diff) |
units: introduce new Documentation= field and make use of it everywhere
This should help making the boot process a bit easier to explore and
understand for the administrator. The simple idea is that "systemctl
status" now shows a link to documentation alongside the other status and
decriptionary information of a service.
This patch adds the necessary fields to all our shipped units if we have
proper documentation for them.
Diffstat (limited to 'src')
-rw-r--r-- | src/core/dbus-unit.c | 1 | ||||
-rw-r--r-- | src/core/dbus-unit.h | 1 | ||||
-rw-r--r-- | src/core/load-fragment-gperf.gperf.m4 | 1 | ||||
-rw-r--r-- | src/core/load-fragment.c | 37 | ||||
-rw-r--r-- | src/core/load-fragment.h | 1 | ||||
-rw-r--r-- | src/core/unit.c | 8 | ||||
-rw-r--r-- | src/core/unit.h | 1 | ||||
-rw-r--r-- | src/shared/util.c | 21 | ||||
-rw-r--r-- | src/shared/util.h | 2 | ||||
-rw-r--r-- | src/systemctl/systemctl.c | 38 |
10 files changed, 108 insertions, 3 deletions
diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index 834fbd7693..812f1b9f16 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -808,6 +808,7 @@ const BusProperty bus_unit_properties[] = { { "PropagateReloadTo", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PROPAGATE_RELOAD_TO]), true }, { "PropagateReloadFrom", bus_unit_append_dependencies, "as", offsetof(Unit, dependencies[UNIT_PROPAGATE_RELOAD_FROM]), true }, { "RequiresMountsFor", bus_property_append_strv, "as", offsetof(Unit, requires_mounts_for), true }, + { "Documentation", bus_property_append_strv, "as", offsetof(Unit, documentation), true }, { "Description", bus_unit_append_description, "s", 0 }, { "LoadState", bus_unit_append_load_state, "s", offsetof(Unit, load_state) }, { "ActiveState", bus_unit_append_active_state, "s", 0 }, diff --git a/src/core/dbus-unit.h b/src/core/dbus-unit.h index d22802d274..9680b56f06 100644 --- a/src/core/dbus-unit.h +++ b/src/core/dbus-unit.h @@ -87,6 +87,7 @@ " <property name=\"PropagateReloadFrom\" type=\"as\" access=\"read\"/>\n" \ " <property name=\"RequiresMountsFor\" type=\"as\" access=\"read\"/>\n" \ " <property name=\"Description\" type=\"s\" access=\"read\"/>\n" \ + " <property name=\"Documentation\" type=\"as\" access=\"read\"/>\n" \ " <property name=\"LoadState\" type=\"s\" access=\"read\"/>\n" \ " <property name=\"ActiveState\" type=\"s\" access=\"read\"/>\n" \ " <property name=\"SubState\" type=\"s\" access=\"read\"/>\n" \ diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index d929273877..f0e25c0c5d 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -92,6 +92,7 @@ $1.ControlGroupPersistent, config_parse_tristate, 0, )m4_dnl Unit.Names, config_parse_unit_names, 0, 0 Unit.Description, config_parse_unit_string_printf, 0, offsetof(Unit, description) +Unit.Documentation, config_parse_documentation, 0, offsetof(Unit, documentation) Unit.Requires, config_parse_unit_deps, UNIT_REQUIRES, 0 Unit.RequiresOverridable, config_parse_unit_deps, UNIT_REQUIRES_OVERRIDABLE, 0 Unit.Requisite, config_parse_unit_deps, UNIT_REQUISITE, 0 diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index c2efec6657..3bc053341c 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -2063,6 +2063,43 @@ int config_parse_unit_requires_mounts_for( return r; } +int config_parse_documentation( + const char *filename, + unsigned line, + const char *section, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + Unit *u = userdata; + int r; + char **a, **b; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(u); + + r = config_parse_unit_strv_printf(filename, line, section, lvalue, ltype, rvalue, data, userdata); + if (r < 0) + return r; + + for (a = b = u->documentation; a && *a; a++) { + + if (is_valid_documentation_url(*a)) + *(b++) = *a; + else { + log_error("[%s:%u] Invalid URL, ignoring: %s", filename, line, *a); + free(*a); + } + } + *b = NULL; + + return r; +} + #define FOLLOW_MAX 8 static int open_follow(char **filename, FILE **_f, Set *names, char **_final) { diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h index ccc436420e..3b2ed096b5 100644 --- a/src/core/load-fragment.h +++ b/src/core/load-fragment.h @@ -36,6 +36,7 @@ int config_parse_unit_names(const char *filename, unsigned line, const char *sec int config_parse_unit_string_printf(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_unit_strv_printf(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_unit_path_printf(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_documentation(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_socket_listen(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_socket_bind(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_exec_nice(const char *filename, unsigned line, const char *section, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/core/unit.c b/src/core/unit.c index 200d196878..1f1a5314f7 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -397,6 +397,7 @@ void unit_free(Unit *u) { cgroup_attribute_free_list(u->cgroup_attributes); free(u->description); + strv_free(u->documentation); free(u->fragment_path); free(u->instance); @@ -624,7 +625,7 @@ const char *unit_description(Unit *u) { } void unit_dump(Unit *u, FILE *f, const char *prefix) { - char *t; + char *t, **j; UnitDependency d; Iterator i; char *p2; @@ -672,6 +673,9 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { SET_FOREACH(t, u->names, i) fprintf(f, "%s\tName: %s\n", prefix, t); + STRV_FOREACH(j, u->documentation) + fprintf(f, "%s\tDocumentation: %s\n", prefix, *j); + if ((following = unit_following(u))) fprintf(f, "%s\tFollowing: %s\n", prefix, following->id); @@ -698,8 +702,6 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) { } if (!strv_isempty(u->requires_mounts_for)) { - char **j; - fprintf(f, "%s\tRequiresMountsFor:", prefix); diff --git a/src/core/unit.h b/src/core/unit.h index e8e6b09866..87dc88c961 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -157,6 +157,7 @@ struct Unit { char **requires_mounts_for; char *description; + char **documentation; char *fragment_path; /* if loaded from a config file this is the primary path to it */ usec_t fragment_mtime; diff --git a/src/shared/util.c b/src/shared/util.c index 0b81e1c4f9..ae0ce320ad 100644 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -5610,3 +5610,24 @@ int can_sleep(const char *type) { free(p); return found; } + +bool is_valid_documentation_url(const char *url) { + assert(url); + + if (startswith(url, "http://") && url[7]) + return true; + + if (startswith(url, "https://") && url[8]) + return true; + + if (startswith(url, "file:") && url[5]) + return true; + + if (startswith(url, "info:") && url[5]) + return true; + + if (startswith(url, "man:") && url[4]) + return true; + + return false; +} diff --git a/src/shared/util.h b/src/shared/util.h index f1bcb8a101..3dce047b40 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -508,4 +508,6 @@ int getenv_for_pid(pid_t pid, const char *field, char **_value); int can_sleep(const char *type); +bool is_valid_documentation_url(const char *url); + #endif diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 92c79d038f..0d2044f874 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -2158,6 +2158,8 @@ typedef struct UnitStatusInfo { const char *description; const char *following; + char **documentation; + const char *path; const char *default_control_group; @@ -2303,6 +2305,19 @@ static void print_status_info(UnitStatusInfo *i) { if (i->what) printf("\t What: %s\n", i->what); + if (!strv_isempty(i->documentation)) { + char **t; + bool first = true; + + STRV_FOREACH(t, i->documentation) { + if (first) { + printf("\t Docs: %s\n", *t); + first = false; + } else + printf("\t %s\n", *t); + } + } + if (i->accept) printf("\tAccepted: %u; Connected: %u\n", i->n_accepted, i->n_connections); @@ -2609,6 +2624,27 @@ static int status_property(const char *name, DBusMessageIter *iter, UnitStatusIn dbus_message_iter_next(&sub); } + } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING && + streq(name, "Documentation")) { + + DBusMessageIter sub; + + dbus_message_iter_recurse(iter, &sub); + while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING) { + const char *s; + char **l; + + dbus_message_iter_get_basic(&sub, &s); + + l = strv_append(i->documentation, s); + if (!l) + return -ENOMEM; + + strv_free(i->documentation); + i->documentation = l; + + dbus_message_iter_next(&sub); + } } break; @@ -2932,6 +2968,8 @@ static int show_one(const char *verb, DBusConnection *bus, const char *path, boo if (!show_properties) print_status_info(&info); + strv_free(info.documentation); + if (!streq_ptr(info.active_state, "active") && !streq_ptr(info.active_state, "reloading") && streq(verb, "status")) |