From 172338d51bfa0cea3e035094d024a82c2ebb2fcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Wed, 9 Sep 2020 11:19:38 +0200 Subject: [PATCH 1/5] systemctl: list unit introspection verbs first, modification second The list was rather ad hoc, with "reset-failed" sandwiched between "help" and "list-dependencies". Since a person will usually either want to introspect state in various ways or modify state in a certain way, let's put all the introspection commands together and all the ones that actually have an effect second. --- man/systemctl.xml | 375 +++++++++++++++++++------------------- src/systemctl/systemctl.c | 22 +-- 2 files changed, 202 insertions(+), 195 deletions(-) diff --git a/man/systemctl.xml b/man/systemctl.xml index 1c55028837..802824d438 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -50,7 +50,7 @@ The following commands are understood: - Unit Commands + Unit Commands (Introspection and Modification) @@ -151,6 +151,196 @@ Sun 2017-02-26 20:57:49 EST 2h 3min left Sun 2017-02-26 11:56:36 EST 6h ago + + is-active PATTERN + + + Check whether any of the specified units are active + (i.e. running). Returns an exit code + 0 if at least one is active, or + non-zero otherwise. Unless is + specified, this will also print the current unit state to + standard output. + + + + + is-failed PATTERN + + + Check whether any of the specified units are in a + "failed" state. Returns an exit code + 0 if at least one has failed, + non-zero otherwise. Unless is + specified, this will also print the current unit state to + standard output. + + + + + status PATTERN…|PID…] + + + Show terse runtime status information about one or + more units, followed by most recent log data from the + journal. If no units are specified, show system status. If + combined with , also show the status of + all units (subject to limitations specified with + ). If a PID is passed, show information + about the unit the process belongs to. + + This function is intended to generate human-readable + output. If you are looking for computer-parsable output, + use show instead. By default, this + function only shows 10 lines of output and ellipsizes + lines to fit in the terminal window. This can be changed + with and , + see above. In addition, journalctl + --unit=NAME or + journalctl + --user-unit=NAME use + a similar filter for messages and might be more + convenient. + + + systemd implicitly loads units as necessary, so just running the status will + attempt to load a file. The command is thus not useful for determining if something was already loaded or + not. The units may possibly also be quickly unloaded after the operation is completed if there's no reason + to keep it in memory thereafter. + + + + Example output from systemctl status + + $ systemctl status bluetooth +● bluetooth.service - Bluetooth service + Loaded: loaded (/usr/lib/systemd/system/bluetooth.service; enabled; vendor preset: enabled) + Active: active (running) since Wed 2017-01-04 13:54:04 EST; 1 weeks 0 days ago + Docs: man:bluetoothd(8) + Main PID: 930 (bluetoothd) + Status: "Running" + Tasks: 1 + Memory: 648.0K + CPU: 435ms + CGroup: /system.slice/bluetooth.service + └─930 /usr/lib/bluetooth/bluetoothd + +Jan 12 10:46:45 example.com bluetoothd[8900]: Not enough free handles to register service +Jan 12 10:46:45 example.com bluetoothd[8900]: Current Time Service could not be registered +Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output error (5) + + + The dot ("●") uses color on supported terminals to summarize the unit state at a glance. White + indicates an inactive or deactivating state. Red indicates a + failed or error state and green indicates an + active, reloading or activating state. + + + The "Loaded:" line in the output will show loaded if the unit has been loaded into + memory. Other possible values for "Loaded:" include: error if there was a problem + loading it, not-found if no unit file was found for this unit, + bad-setting if an essential unit file setting could not be parsed and + masked if the unit file has been masked. Along with showing the path to the unit file, + this line will also show the enablement state. Enabled commands start at boot. See the full table of + possible enablement states — including the definition of masked — in the documentation + for the is-enabled command. + + + The "Active:" line shows active state. The value is usually active or + inactive. Active could mean started, bound, plugged in, etc depending on the unit type. + The unit could also be in process of changing states, reporting a state of activating or + deactivating. A special failed state is entered when the service + failed in some way, such as a crash, exiting with an error code or timing out. If the failed state is + entered the cause will be logged for later reference. + + + + + + + show PATTERN…|JOB + + + Show properties of one or more units, jobs, or the manager itself. If no argument is specified, + properties of the manager will be shown. If a unit name is specified, properties of the unit are shown, and + if a job ID is specified, properties of the job are shown. By default, empty properties are suppressed. Use + to show those too. To select specific properties to show, use + . This command is intended to be used whenever computer-parsable output is + required. Use status if you are looking for formatted human-readable output. + + Many properties shown by systemctl show map directly to configuration settings of + the system and service manager and its unit files. Note that the properties shown by the command are + generally more low-level, normalized versions of the original configuration settings and expose runtime + state in addition to configuration. For example, properties shown for service units include the service's + current main process identifier as MainPID (which is runtime state), and time settings + are always exposed as properties ending in the …USec suffix even if a matching + configuration options end in …Sec, because microseconds is the normalized time unit used + internally by the system and service manager. + + For details about many of these properties, see the documentation of the D-Bus interface + backing these properties, see + org.freedesktop.systemd15. + + + + + cat PATTERN + + + Show backing files of one or more units. Prints the + "fragment" and "drop-ins" (source files) of units. Each + file is preceded by a comment which includes the file + name. Note that this shows the contents of the backing files + on disk, which may not match the system manager's + understanding of these units if any unit files were + updated on disk and the daemon-reload + command wasn't issued since. + + + + + help PATTERN…|PID + + + Show manual pages for one or more units, if + available. If a PID is given, the manual pages for the unit + the process belongs to are shown. + + + + + + list-dependencies + UNIT... + + + + Shows units required and wanted by the specified + units. This recursively lists units following the + Requires=, + Requisite=, + ConsistsOf=, + Wants=, BindsTo= + dependencies. If no units are specified, + default.target is implied. + + By default, only target units are recursively + expanded. When is passed, all other + units are recursively expanded as well. + + Options , + , + may be used to change what types of dependencies + are shown. + + Note that this command only lists units currently loaded into memory by the service manager. In + particular, this command is not suitable to get a comprehensive list at all reverse dependencies on a + specific unit, as it won't list the dependencies declared by units currently not loaded. + + + + + start PATTERN @@ -325,148 +515,6 @@ Sun 2017-02-26 20:57:49 EST 2h 3min left Sun 2017-02-26 11:56:36 EST 6h ago processes in the unit's cgroup. - - is-active PATTERN - - - Check whether any of the specified units are active - (i.e. running). Returns an exit code - 0 if at least one is active, or - non-zero otherwise. Unless is - specified, this will also print the current unit state to - standard output. - - - - is-failed PATTERN - - - Check whether any of the specified units are in a - "failed" state. Returns an exit code - 0 if at least one has failed, - non-zero otherwise. Unless is - specified, this will also print the current unit state to - standard output. - - - - status PATTERN…|PID…] - - - Show terse runtime status information about one or - more units, followed by most recent log data from the - journal. If no units are specified, show system status. If - combined with , also show the status of - all units (subject to limitations specified with - ). If a PID is passed, show information - about the unit the process belongs to. - - This function is intended to generate human-readable - output. If you are looking for computer-parsable output, - use show instead. By default, this - function only shows 10 lines of output and ellipsizes - lines to fit in the terminal window. This can be changed - with and , - see above. In addition, journalctl - --unit=NAME or - journalctl - --user-unit=NAME use - a similar filter for messages and might be more - convenient. - - - systemd implicitly loads units as necessary, so just running the status will - attempt to load a file. The command is thus not useful for determining if something was already loaded or - not. The units may possibly also be quickly unloaded after the operation is completed if there's no reason - to keep it in memory thereafter. - - - - Example output from systemctl status - - $ systemctl status bluetooth -● bluetooth.service - Bluetooth service - Loaded: loaded (/usr/lib/systemd/system/bluetooth.service; enabled; vendor preset: enabled) - Active: active (running) since Wed 2017-01-04 13:54:04 EST; 1 weeks 0 days ago - Docs: man:bluetoothd(8) - Main PID: 930 (bluetoothd) - Status: "Running" - Tasks: 1 - Memory: 648.0K - CPU: 435ms - CGroup: /system.slice/bluetooth.service - └─930 /usr/lib/bluetooth/bluetoothd - -Jan 12 10:46:45 example.com bluetoothd[8900]: Not enough free handles to register service -Jan 12 10:46:45 example.com bluetoothd[8900]: Current Time Service could not be registered -Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output error (5) - - - The dot ("●") uses color on supported terminals to summarize the unit state at a glance. White - indicates an inactive or deactivating state. Red indicates a - failed or error state and green indicates an - active, reloading or activating state. - - - The "Loaded:" line in the output will show loaded if the unit has been loaded into - memory. Other possible values for "Loaded:" include: error if there was a problem - loading it, not-found if no unit file was found for this unit, - bad-setting if an essential unit file setting could not be parsed and - masked if the unit file has been masked. Along with showing the path to the unit file, - this line will also show the enablement state. Enabled commands start at boot. See the full table of - possible enablement states — including the definition of masked — in the documentation - for the is-enabled command. - - - The "Active:" line shows active state. The value is usually active or - inactive. Active could mean started, bound, plugged in, etc depending on the unit type. - The unit could also be in process of changing states, reporting a state of activating or - deactivating. A special failed state is entered when the service - failed in some way, such as a crash, exiting with an error code or timing out. If the failed state is - entered the cause will be logged for later reference. - - - - - - show PATTERN…|JOB - - - Show properties of one or more units, jobs, or the manager itself. If no argument is specified, - properties of the manager will be shown. If a unit name is specified, properties of the unit are shown, and - if a job ID is specified, properties of the job are shown. By default, empty properties are suppressed. Use - to show those too. To select specific properties to show, use - . This command is intended to be used whenever computer-parsable output is - required. Use status if you are looking for formatted human-readable output. - - Many properties shown by systemctl show map directly to configuration settings of - the system and service manager and its unit files. Note that the properties shown by the command are - generally more low-level, normalized versions of the original configuration settings and expose runtime - state in addition to configuration. For example, properties shown for service units include the service's - current main process identifier as MainPID (which is runtime state), and time settings - are always exposed as properties ending in the …USec suffix even if a matching - configuration options end in …Sec, because microseconds is the normalized time unit used - internally by the system and service manager. - - For details about many of these properties, see the documentation of the D-Bus interface - backing these properties, see - org.freedesktop.systemd15. - - - - cat PATTERN - - - Show backing files of one or more units. Prints the - "fragment" and "drop-ins" (source files) of units. Each - file is preceded by a comment which includes the file - name. Note that this shows the contents of the backing files - on disk, which may not match the system manager's - understanding of these units if any unit files were - updated on disk and the daemon-reload - command wasn't issued since. - - set-property UNIT PROPERTY=VALUE @@ -502,16 +550,6 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err - - help PATTERN…|PID - - - Show manual pages for one or more units, if - available. If a PID is given, the manual pages for the unit - the process belongs to are shown. - - - reset-failed [PATTERN…] @@ -529,37 +567,6 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err to be started again, use this command to make it startable again. - - - - list-dependencies - UNIT... - - - - Shows units required and wanted by the specified - units. This recursively lists units following the - Requires=, - Requisite=, - ConsistsOf=, - Wants=, BindsTo= - dependencies. If no units are specified, - default.target is implied. - - By default, only target units are recursively - expanded. When is passed, all other - units are recursively expanded as well. - - Options , - , - may be used to change what types of dependencies - are shown. - - Note that this command only lists units currently loaded into memory by the service manager. In - particular, this command is not suitable to get a comprehensive list at all reverse dependencies on a - specific unit, as it won't list the dependencies declared by units currently not loaded. - - diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index acf07d645f..7057049c60 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -7708,6 +7708,16 @@ static int systemctl_help(void) { " ordered by address\n" " list-timers [PATTERN...] List timer units currently in memory,\n" " ordered by next elapse\n" + " is-active PATTERN... Check whether units are active\n" + " is-failed PATTERN... Check whether units are failed\n" + " status [PATTERN...|PID...] Show runtime status of one or more units\n" + " show [PATTERN...|JOB...] Show properties of one or more\n" + " units/jobs or the manager\n" + " cat PATTERN... Show files and drop-ins of specified units\n" + " help PATTERN...|PID... Show manual for one or more units\n" + " list-dependencies [UNIT...] Recursively show units which are required\n" + " or wanted by the units or by which those\n" + " units are required or wanted\n" " start UNIT... Start (activate) one or more units\n" " stop UNIT... Stop (deactivate) one or more units\n" " reload UNIT... Reload one or more units\n" @@ -7723,19 +7733,9 @@ static int systemctl_help(void) { " configuration of unit\n" " freeze PATTERN... Freeze execution of unit processes\n" " thaw PATTERN... Resume execution of a frozen unit\n" - " is-active PATTERN... Check whether units are active\n" - " is-failed PATTERN... Check whether units are failed\n" - " status [PATTERN...|PID...] Show runtime status of one or more units\n" - " show [PATTERN...|JOB...] Show properties of one or more\n" - " units/jobs or the manager\n" - " cat PATTERN... Show files and drop-ins of specified units\n" " set-property UNIT PROPERTY=VALUE... Sets one or more properties of a unit\n" - " help PATTERN...|PID... Show manual for one or more units\n" " reset-failed [PATTERN...] Reset failed state for all, one, or more\n" - " units\n" - " list-dependencies [UNIT...] Recursively show units which are required\n" - " or wanted by the units or by which those\n" - " units are required or wanted" + " units" "\n%3$sUnit File Commands:%4$s\n" " list-unit-files [PATTERN...] List installed unit files\n" " enable [UNIT...|PATH...] Enable one or more unit files\n" From 6824c132e95ac74cface6c1e2651fa156616faaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Mon, 7 Sep 2020 21:25:24 +0200 Subject: [PATCH 2/5] systemctl: add service-log-{level,target} verbs Heavily inspired by #15622. This adds: systemctl service-log-level systemd-resolved systemctl service-log-level systemd-resolved info systemctl service-log-target systemd-resolved systemctl service-log-target systemd-resolved console We already have systemctl verbs log-level, log-target, and service-watchdogs. Those two new verbs tie nicely into this scheme. --- man/org.freedesktop.LogControl1.xml | 39 +++++++++--- man/systemctl.xml | 56 +++++++++++++++++ man/systemd.service.xml | 9 +-- src/systemctl/systemctl.c | 96 +++++++++++++++++++++++++++++ 4 files changed, 188 insertions(+), 12 deletions(-) diff --git a/man/org.freedesktop.LogControl1.xml b/man/org.freedesktop.LogControl1.xml index 5944061f99..a51b7b01e1 100644 --- a/man/org.freedesktop.LogControl1.xml +++ b/man/org.freedesktop.LogControl1.xml @@ -24,8 +24,8 @@ Introduction org.freedesktop.LogControl1 is a generic interface that is intended - to be used by any daemon which should allow setting the log level and target over D-Bus. It is implemented - by various daemons that are part of the + to be used by any daemon which allows the log level and target to be set over D-Bus. It is implemented by + various daemons that are part of the systemd1 suite. It is assumed that those settings are global for the whole program, so a fixed object path is @@ -95,12 +95,35 @@ node /org/freedesktop/LogControl1 { It is a short string that identifies the program that is the source of log messages that is passed to the syslog3 call. - - Note: journalctl option / may - be used to filter log messages by log level, option / - may be used to by the syslog identifier, and filters like _TRANSPORT=syslog, - _TRANSPORT=journal, and _TRANSPORT=kernel may be used to filter - messages by the mechanism through which they reached systemd-journald. + + + Tools + + journalctl option / may be used + to filter log messages by log level, option / may be + used to by the syslog identifier, and filters like _TRANSPORT=syslog, + _TRANSPORT=journal, and _TRANSPORT=kernel may be used to filter + messages by the mechanism through which they reached systemd-journald. + + systemctl log-level and systemctl log-target verbs may be + used to query and set the LogLevel and LogTarget properties of the + service manager. systemctl service-log-level and systemctl + service-log-target may similarly be used for individual services. (Services must have the + BusName= property set and must implement the interface described here. See + systemd.service5 + for details about BusName=.) + + + + See Also + + systemd1, + journalctl1, + systemctl1, + systemd.service5, + syslog3 + + diff --git a/man/systemctl.xml b/man/systemctl.xml index 802824d438..dc02fdcb86 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -550,6 +550,62 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err + + service-log-level SERVICE [LEVEL] + + If the LEVEL argument is not given, print the current + log level as reported by service SERVICE. + + If the optional argument LEVEL is provided, then change the + current log level of the service to LEVEL. The log level should be a + typical syslog log level, i.e. a value in the range 0…7 or one of the strings + emerg, alert, crit, + err, warning, notice, + info, debug; see syslog3 + for details. + + The service must have the appropriate + BusName=destination property and also implement the + generic + org.freedesktop.LogControl15 + interface. (systemctl will use the generic D-Bus protocol to access the + org.freedesktop.LogControl1.LogLevel interface for the D-Bus name + destination.) + + + + service-log-target SERVICE [TARGET] + + If the TARGET argument is not given, print the current + log target as reported by service SERVICE. + + If the optional argument TARGET is provided, then change the + current log target of the service to TARGET. The log target should be + one of the strings console (for log output to the service's standard error + stream), kmsg (for log output to the kernel log buffer), + journal (for log output to + systemd-journald.service8 + using the native journal protocol), syslog (for log output to the classic + syslog socket /dev/log), null (for no log output + whatsoever) or auto (for an automatically determined choice, typically + equivalent to console if the service is invoked interactively, and + journal or syslog otherwise). + + For most services, only a small subset of log targets make sense. In particular, most + "normal" services should only implement console, journal, + and null. Anything else is only appropriate for low-level services that + are active in very early boot before proper logging is established. + + The service must have the appropriate + BusName=destination property and also implement the + generic + org.freedesktop.LogControl15 + interface. (systemctl will use the generic D-Bus protocol to access the + org.freedesktop.LogControl1.LogLevel interface for the D-Bus name + destination.) + + reset-failed [PATTERN…] diff --git a/man/systemd.service.xml b/man/systemd.service.xml index 124fa383ab..d11e37da84 100644 --- a/man/systemd.service.xml +++ b/man/systemd.service.xml @@ -292,10 +292,11 @@ BusName= - Takes a D-Bus bus name that this service is - reachable as. This option is mandatory for services where - Type= is set to - . + Takes a D-Bus destination name that this service shall use. This option is mandatory + for services where Type= is set to . It is recommended to + always set this property if known to make it easy to map the service name to the D-Bus destination. + In particular, systemctl service-log-level/service-log-target verbs make use of + this. diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 7057049c60..5f540eabb8 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -6327,6 +6327,98 @@ static int log_level(int argc, char *argv[], void *userdata) { return 0; } +static int service_name_to_dbus(sd_bus *bus, const char *name, char **ret_dbus_name) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_free_ char *bus_name = NULL; + int r; + + /* First, look for the BusName= property */ + _cleanup_free_ char *dbus_path = unit_dbus_path_from_name(name); + if (!dbus_path) + return log_oom(); + + r = sd_bus_get_property_string( + bus, + "org.freedesktop.systemd1", + dbus_path, + "org.freedesktop.systemd1.Service", + "BusName", + &error, + &bus_name); + if (r < 0) + return log_error_errno(r, "Failed to obtain BusName= property of %s: %s", + name, bus_error_message(&error, r)); + + if (isempty(bus_name)) + return log_error_errno(SYNTHETIC_ERRNO(ENOLINK), + "Unit %s doesn't declare BusName=.", name); + + *ret_dbus_name = TAKE_PTR(bus_name); + return 0; +} + +static int service_log_setting(int argc, char *argv[], void *userdata) { + sd_bus *bus; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_free_ char *unit = NULL, *dbus_name = NULL; + int r; + + assert(STR_IN_SET(argv[0], "service-log-level", "service-log-target")); + bool level = streq(argv[0], "service-log-level"); + + r = acquire_bus(BUS_FULL, &bus); + if (r < 0) + return r; + + r = unit_name_mangle_with_suffix(argv[1], argv[0], + arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN, + ".service", &unit); + if (r < 0) + return log_error_errno(r, "Failed to mangle unit name: %m"); + + r = service_name_to_dbus(bus, unit, &dbus_name); + if (r < 0) + return r; + + if (argc == 2) { + _cleanup_free_ char *value = NULL; + + r = sd_bus_get_property_string( + bus, + dbus_name, + "/org/freedesktop/LogControl1", + "org.freedesktop.LogControl1", + level ? "LogLevel" : "LogTarget", + &error, + &value); + if (r < 0) + return log_error_errno(r, "Failed to get log %s of service %s: %s", + level ? "level" : "target", + dbus_name, bus_error_message(&error, r)); + + puts(value); + + } else { + assert(argc == 3); + + r = sd_bus_set_property( + bus, + dbus_name, + "/org/freedesktop/LogControl1", + "org.freedesktop.LogControl1", + level ? "LogLevel" : "LogTarget", + &error, + "s", + argv[2]); + if (r < 0) + return log_error_errno(r, "Failed to set log %s of service %s to %s: %s", + level ? "level" : "target", + dbus_name, argv[2], bus_error_message(&error, r)); + } + + return 0; +} + static int log_target(int argc, char *argv[], void *userdata) { sd_bus *bus; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -7734,6 +7826,8 @@ static int systemctl_help(void) { " freeze PATTERN... Freeze execution of unit processes\n" " thaw PATTERN... Resume execution of a frozen unit\n" " set-property UNIT PROPERTY=VALUE... Sets one or more properties of a unit\n" + " service-log-level SERVICE [LEVEL] Get/set logging threshold for service\n" + " service-log-target SERVICE [TARGET] Get/set logging target for service\n" " reset-failed [PATTERN...] Reset failed state for all, one, or more\n" " units" "\n%3$sUnit File Commands:%4$s\n" @@ -9034,6 +9128,8 @@ static int systemctl_main(int argc, char *argv[]) { { "daemon-reexec", VERB_ANY, 1, VERB_ONLINE_ONLY, daemon_reload }, { "log-level", VERB_ANY, 2, VERB_ONLINE_ONLY, log_level }, { "log-target", VERB_ANY, 2, VERB_ONLINE_ONLY, log_target }, + { "service-log-level", 2, 3, VERB_ONLINE_ONLY, service_log_setting }, + { "service-log-target", 2, 3, VERB_ONLINE_ONLY, service_log_setting }, { "service-watchdogs", VERB_ANY, 2, VERB_ONLINE_ONLY, service_watchdogs }, { "show-environment", VERB_ANY, 1, VERB_ONLINE_ONLY, show_environment }, { "set-environment", 2, VERB_ANY, VERB_ONLINE_ONLY, set_environment }, From 77db9433a777f84df17b161080049bb5ec9a1dfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Fri, 11 Sep 2020 14:47:44 +0200 Subject: [PATCH 3/5] systemctl: merge log_target(), log_level(), service_log_setting() --- src/systemctl/systemctl.c | 135 +++++++++++++------------------------- 1 file changed, 45 insertions(+), 90 deletions(-) diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 5f540eabb8..86de4848a5 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -6298,33 +6298,49 @@ static int switch_root(int argc, char *argv[], void *userdata) { return 0; } -static int log_level(int argc, char *argv[], void *userdata) { - sd_bus *bus; +static int log_setting_internal(sd_bus *bus, const BusLocator* bloc, const char *verb, const char *value) { + assert(bus); + assert(STR_IN_SET(verb, "log-level", "log-target", "service-log-level", "service-log-target")); + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + bool level = endswith(verb, "log-level"); int r; + if (value) { + r = bus_set_property(bus, bloc, + level ? "LogLevel" : "LogTarget", + &error, "s", value); + if (r < 0) + return log_error_errno(r, "Failed to set log %s of %s to %s: %s", + level ? "level" : "target", + bloc->destination, value, bus_error_message(&error, r)); + } else { + _cleanup_free_ char *t = NULL; + + r = bus_get_property_string(bus, bloc, + level ? "LogLevel" : "LogTarget", + &error, &t); + if (r < 0) + return log_error_errno(r, "Failed to get log %s of %s: %s", + level ? "level" : "target", + bloc->destination, bus_error_message(&error, r)); + puts(t); + } + + return 0; +} + +static int log_setting(int argc, char *argv[], void *userdata) { + sd_bus *bus; + int r; + + assert(argc >= 1 && argc <= 2); + r = acquire_bus(BUS_MANAGER, &bus); if (r < 0) return r; - if (argc == 1) { - _cleanup_free_ char *level = NULL; - - r = bus_get_property_string(bus, bus_systemd_mgr, "LogLevel", &error, &level); - if (r < 0) - return log_error_errno(r, "Failed to get log level: %s", bus_error_message(&error, r)); - - puts(level); - - } else { - assert(argc == 2); - - r = bus_set_property(bus, bus_systemd_mgr, "LogLevel", &error, "s", argv[1]); - if (r < 0) - return log_error_errno(r, "Failed to set log level: %s", bus_error_message(&error, r)); - } - - return 0; + return log_setting_internal(bus, bus_systemd_mgr, argv[0], argv[1]); } static int service_name_to_dbus(sd_bus *bus, const char *name, char **ret_dbus_name) { @@ -6359,12 +6375,10 @@ static int service_name_to_dbus(sd_bus *bus, const char *name, char **ret_dbus_n static int service_log_setting(int argc, char *argv[], void *userdata) { sd_bus *bus; - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_free_ char *unit = NULL, *dbus_name = NULL; int r; - assert(STR_IN_SET(argv[0], "service-log-level", "service-log-target")); - bool level = streq(argv[0], "service-log-level"); + assert(argc >= 2 && argc <= 3); r = acquire_bus(BUS_FULL, &bus); if (r < 0) @@ -6380,72 +6394,13 @@ static int service_log_setting(int argc, char *argv[], void *userdata) { if (r < 0) return r; - if (argc == 2) { - _cleanup_free_ char *value = NULL; + const BusLocator bloc = { + .destination = dbus_name, + .path = "/org/freedesktop/LogControl1", + .interface = "org.freedesktop.LogControl1", + }; - r = sd_bus_get_property_string( - bus, - dbus_name, - "/org/freedesktop/LogControl1", - "org.freedesktop.LogControl1", - level ? "LogLevel" : "LogTarget", - &error, - &value); - if (r < 0) - return log_error_errno(r, "Failed to get log %s of service %s: %s", - level ? "level" : "target", - dbus_name, bus_error_message(&error, r)); - - puts(value); - - } else { - assert(argc == 3); - - r = sd_bus_set_property( - bus, - dbus_name, - "/org/freedesktop/LogControl1", - "org.freedesktop.LogControl1", - level ? "LogLevel" : "LogTarget", - &error, - "s", - argv[2]); - if (r < 0) - return log_error_errno(r, "Failed to set log %s of service %s to %s: %s", - level ? "level" : "target", - dbus_name, argv[2], bus_error_message(&error, r)); - } - - return 0; -} - -static int log_target(int argc, char *argv[], void *userdata) { - sd_bus *bus; - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - int r; - - r = acquire_bus(BUS_MANAGER, &bus); - if (r < 0) - return r; - - if (argc == 1) { - _cleanup_free_ char *target = NULL; - - r = bus_get_property_string(bus, bus_systemd_mgr, "LogTarget", &error, &target); - if (r < 0) - return log_error_errno(r, "Failed to get log target: %s", bus_error_message(&error, r)); - - puts(target); - - } else { - assert(argc == 2); - - r = bus_set_property(bus, bus_systemd_mgr, "LogTarget", &error, "s", argv[1]); - if (r < 0) - return log_error_errno(r, "Failed to set log target: %s", bus_error_message(&error, r)); - } - - return 0; + return log_setting_internal(bus, &bloc, argv[0], argv[2]); } static int service_watchdogs(int argc, char *argv[], void *userdata) { @@ -9126,8 +9081,8 @@ static int systemctl_main(int argc, char *argv[]) { { "help", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, show }, { "daemon-reload", VERB_ANY, 1, VERB_ONLINE_ONLY, daemon_reload }, { "daemon-reexec", VERB_ANY, 1, VERB_ONLINE_ONLY, daemon_reload }, - { "log-level", VERB_ANY, 2, VERB_ONLINE_ONLY, log_level }, - { "log-target", VERB_ANY, 2, VERB_ONLINE_ONLY, log_target }, + { "log-level", VERB_ANY, 2, VERB_ONLINE_ONLY, log_setting }, + { "log-target", VERB_ANY, 2, VERB_ONLINE_ONLY, log_setting }, { "service-log-level", 2, 3, VERB_ONLINE_ONLY, service_log_setting }, { "service-log-target", 2, 3, VERB_ONLINE_ONLY, service_log_setting }, { "service-watchdogs", VERB_ANY, 2, VERB_ONLINE_ONLY, service_watchdogs }, From d94bab0805cd3007414ee6ff33e2a24d585f1217 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Fri, 11 Sep 2020 15:42:23 +0200 Subject: [PATCH 4/5] systemctl: immediately reject invalid log levels Symbolic names and number in the appropriate range are allowed (log_level_from_string() DTRT already). The target names are more messy, so we leave the verification to the service. --- src/systemctl/systemctl.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 86de4848a5..4e31dc7889 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -81,6 +81,7 @@ #include "stat-util.h" #include "string-table.h" #include "strv.h" +#include "syslog-util.h" #include "sysv-compat.h" #include "terminal-util.h" #include "tmpfile-util.h" @@ -6307,6 +6308,12 @@ static int log_setting_internal(sd_bus *bus, const BusLocator* bloc, const char int r; if (value) { + if (level) { + if (log_level_from_string(value) < 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "\"%s\" is not a valid log level.", value); + } + r = bus_set_property(bus, bloc, level ? "LogLevel" : "LogTarget", &error, "s", value); From e6e691aebf6d9eaf26657675b49beb8dc81f74d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Fri, 11 Sep 2020 16:00:22 +0200 Subject: [PATCH 5/5] systemctl: give a nice hint about org.freedesktop.LogControl1 when applicable --- src/systemctl/systemctl.c | 50 +++++++++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 4e31dc7889..aa046c6ada 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -6299,6 +6299,18 @@ static int switch_root(int argc, char *argv[], void *userdata) { return 0; } +static void give_log_control1_hint(const char *name) { + _cleanup_free_ char *link = NULL; + + if (arg_quiet) + return; + + (void) terminal_urlify_man("org.freedesktop.LogControl1", "5", &link); + + log_notice("Hint: the service must declare BusName= and implement the appropriate D-Bus interface.\n" + " See the %s for details.", link ?: "org.freedesktop.LogControl1(5) man page"); +} + static int log_setting_internal(sd_bus *bus, const BusLocator* bloc, const char *verb, const char *value) { assert(bus); assert(STR_IN_SET(verb, "log-level", "log-target", "service-log-level", "service-log-target")); @@ -6317,24 +6329,34 @@ static int log_setting_internal(sd_bus *bus, const BusLocator* bloc, const char r = bus_set_property(bus, bloc, level ? "LogLevel" : "LogTarget", &error, "s", value); - if (r < 0) - return log_error_errno(r, "Failed to set log %s of %s to %s: %s", - level ? "level" : "target", - bloc->destination, value, bus_error_message(&error, r)); + if (r >= 0) + return 0; + + log_error_errno(r, "Failed to set log %s of %s to %s: %s", + level ? "level" : "target", + bloc->destination, value, bus_error_message(&error, r)); } else { _cleanup_free_ char *t = NULL; r = bus_get_property_string(bus, bloc, level ? "LogLevel" : "LogTarget", &error, &t); - if (r < 0) - return log_error_errno(r, "Failed to get log %s of %s: %s", - level ? "level" : "target", - bloc->destination, bus_error_message(&error, r)); - puts(t); + if (r >= 0) { + puts(t); + return 0; + } + + log_error_errno(r, "Failed to get log %s of %s: %s", + level ? "level" : "target", + bloc->destination, bus_error_message(&error, r)); } - return 0; + if (sd_bus_error_has_names(&error, SD_BUS_ERROR_UNKNOWN_METHOD, + SD_BUS_ERROR_UNKNOWN_OBJECT, + SD_BUS_ERROR_UNKNOWN_INTERFACE, + SD_BUS_ERROR_UNKNOWN_PROPERTY)) + give_log_control1_hint(bloc->destination); + return r; } static int log_setting(int argc, char *argv[], void *userdata) { @@ -6372,9 +6394,11 @@ static int service_name_to_dbus(sd_bus *bus, const char *name, char **ret_dbus_n return log_error_errno(r, "Failed to obtain BusName= property of %s: %s", name, bus_error_message(&error, r)); - if (isempty(bus_name)) - return log_error_errno(SYNTHETIC_ERRNO(ENOLINK), - "Unit %s doesn't declare BusName=.", name); + if (isempty(bus_name)) { + log_error("Unit %s doesn't declare BusName=.", name); + give_log_control1_hint(name); + return -ENOLINK; + } *ret_dbus_name = TAKE_PTR(bus_name); return 0;