diff --git a/docs/TRANSIENT-SETTINGS.md b/docs/TRANSIENT-SETTINGS.md index 4992acbf67..d5c9a33df8 100644 --- a/docs/TRANSIENT-SETTINGS.md +++ b/docs/TRANSIENT-SETTINGS.md @@ -21,88 +21,131 @@ Most generic unit settings are available for transient units. ``` ✓ Description= -✓ Documentation= ✓ SourcePath= -✓ Requires= -✓ Requisite= -✓ Wants= -✓ BindsTo= -✓ Conflicts= -✓ Before= -✓ After= -✓ OnFailure= -✓ PropagatesReloadTo= -✓ ReloadPropagatedFrom= -✓ PartOf= -✓ Upholds= -✓ JoinsNamespaceOf= -✓ RequiresMountsFor= +✓ OnFailureJobMode= +✓ JobTimeoutAction= +✓ JobTimeoutRebootArgument= +✓ StartLimitAction= +✓ FailureAction= +✓ SuccessAction= +✓ RebootArgument= +✓ CollectMode= ✓ StopWhenUnneeded= ✓ RefuseManualStart= ✓ RefuseManualStop= ✓ AllowIsolate= -✓ DefaultDependencies= -✓ OnFailureJobMode= ✓ IgnoreOnIsolate= +✓ SurviveFinalKillSignal= +✓ DefaultDependencies= ✓ JobTimeoutSec= ✓ JobRunningTimeoutSec= -✓ JobTimeoutAction= -✓ JobTimeoutRebootArgument= ✓ StartLimitIntervalSec= ✓ StartLimitBurst= -✓ StartLimitAction= -✓ FailureAction= -✓ SuccessAction= -✓ FailureActionExitStatus= ✓ SuccessActionExitStatus= -✓ RebootArgument= +✓ FailureActionExitStatus= +✓ Documentation= +✓ RequiresMountsFor= +✓ WantsMountsFor= +✓ Markers= +✓ Requires= +✓ Requisite= +✓ Wants= +✓ BindsTo= +✓ PartOf= +✓ Upholds= +✓ RequiredBy= +✓ RequisiteOf= +✓ WantedBy= +✓ BoundBy= +✓ UpheldBy= +✓ ConsistsOf= +✓ Conflicts= +✓ ConflictedBy= +✓ Before= +✓ After= +✓ OnSuccess= +✓ OnSuccessOf= +✓ OnFailure= +✓ OnFailureOf= +✓ Triggers= +✓ TriggeredBy= +✓ PropagatesReloadTo= +✓ ReloadPropagatedFrom= +✓ PropagatesStopTo= +✓ StopPropagatedFrom= +✓ JoinsNamespaceOf= +✓ References= +✓ ReferencedBy= +✓ InSlice= +✓ SliceOf= +✓ ConditionArchitecture= +✓ ConditionFirmware= +✓ ConditionVirtualization= +✓ ConditionHost= +✓ ConditionKernelCommandLine= +✓ ConditionVersion= +✓ ConditionCredential= +✓ ConditionSecurity= +✓ ConditionCapability= +✓ ConditionACPower= +✓ ConditionNeedsUpdate= +✓ ConditionFirstBoot= ✓ ConditionPathExists= ✓ ConditionPathExistsGlob= ✓ ConditionPathIsDirectory= ✓ ConditionPathIsSymbolicLink= ✓ ConditionPathIsMountPoint= ✓ ConditionPathIsReadWrite= +✓ ConditionPathIsEncrypted= ✓ ConditionDirectoryNotEmpty= ✓ ConditionFileNotEmpty= ✓ ConditionFileIsExecutable= -✓ ConditionNeedsUpdate= -✓ ConditionFirstBoot= -✓ ConditionKernelCommandLine= -✓ ConditionKernelVersion= -✓ ConditionVersion= -✓ ConditionArchitecture= -✓ ConditionFirmware= -✓ ConditionVirtualization= -✓ ConditionSecurity= -✓ ConditionCapability= -✓ ConditionHost= -✓ ConditionACPower= ✓ ConditionUser= ✓ ConditionGroup= ✓ ConditionControlGroupController= +✓ ConditionCPUs= +✓ ConditionMemory= +✓ ConditionEnvironment= +✓ ConditionCPUFeature= +✓ ConditionOSRelease= +✓ ConditionMemoryPressure= +✓ ConditionCPUPressure= +✓ ConditionIOPressure= +✓ ConditionKernelModuleLoaded= +✓ AssertArchitecture= +✓ AssertFirmware= +✓ AssertVirtualization= +✓ AssertHost= +✓ AssertKernelCommandLine= +✓ AssertVersion= +✓ AssertCredential= +✓ AssertSecurity= +✓ AssertCapability= +✓ AssertACPower= +✓ AssertNeedsUpdate= +✓ AssertFirstBoot= ✓ AssertPathExists= ✓ AssertPathExistsGlob= ✓ AssertPathIsDirectory= ✓ AssertPathIsSymbolicLink= ✓ AssertPathIsMountPoint= ✓ AssertPathIsReadWrite= +✓ AssertPathIsEncrypted= ✓ AssertDirectoryNotEmpty= ✓ AssertFileNotEmpty= ✓ AssertFileIsExecutable= -✓ AssertNeedsUpdate= -✓ AssertFirstBoot= -✓ AssertKernelCommandLine= -✓ AssertKernelVersion= -✓ AssertArchitecture= -✓ AssertVirtualization= -✓ AssertSecurity= -✓ AssertCapability= -✓ AssertHost= -✓ AssertACPower= ✓ AssertUser= ✓ AssertGroup= ✓ AssertControlGroupController= -✓ CollectMode= +✓ AssertCPUs= +✓ AssertMemory= +✓ AssertEnvironment= +✓ AssertCPUFeature= +✓ AssertOSRelease= +✓ AssertMemoryPressure= +✓ AssertCPUPressure= +✓ AssertIOPressure= +✓ AssertKernelModuleLoaded= ``` ## Execution-Related Settings @@ -236,55 +279,58 @@ All execution-related settings are available for transient units. All cgroup/resource control settings are available for transient units ``` -✓ CPUAccounting= +✓ DevicePolicy= +✓ Slice= +✓ ManagedOOMSwap= +✓ ManagedOOMMemoryPressure= +✓ ManagedOOMPreference= +✓ MemoryPressureWatch= +✓ DelegateSubgroup= +✓ ManagedOOMMemoryPressureLimit= +✓ MemoryAccounting= +✓ MemoryZSwapWriteback= +✓ IOAccounting= +✓ TasksAccounting= +✓ IPAccounting= +✓ CoredumpReceive= ✓ CPUWeight= ✓ StartupCPUWeight= -✓ CPUShares= -✓ StartupCPUShares= -✓ CPUQuota= -✓ CPUQuotaPeriodSec= +✓ IOWeight= +✓ StartupIOWeight= ✓ AllowedCPUs= ✓ StartupAllowedCPUs= ✓ AllowedMemoryNodes= ✓ StartupAllowedMemoryNodes= -✓ MemoryAccounting= -✓ DefaultMemoryMin= +✓ DisableControllers= +✓ Delegate= ✓ MemoryMin= ✓ DefaultMemoryLow= +✓ DefaultMemoryMin= ✓ MemoryLow= ✓ MemoryHigh= ✓ MemoryMax= ✓ MemorySwapMax= -✓ MemoryLimit= +✓ MemoryZSwapMax= +✓ TasksMax= +✓ CPUQuota= +✓ CPUQuotaPeriodSec= ✓ DeviceAllow= -✓ DevicePolicy= -✓ IOAccounting= -✓ IOWeight= -✓ StartupIOWeight= ✓ IODeviceWeight= +✓ IODeviceLatencyTargetSec= +✓ IPAddressAllow= +✓ IPAddressDeny= +✓ IPIngressFilterPath= +✓ IPEgressFilterPath= +✓ BPFProgram= +✓ SocketBindAllow= +✓ SocketBindDeny= +✓ MemoryPressureThresholdSec= +✓ NFTSet= +✓ ManagedOOMMemoryPressureDurationSec= ✓ IOReadBandwidthMax= ✓ IOWriteBandwidthMax= ✓ IOReadIOPSMax= ✓ IOWriteIOPSMax= -✓ BlockIOAccounting= -✓ BlockIOWeight= -✓ StartupBlockIOWeight= -✓ BlockIODeviceWeight= -✓ BlockIOReadBandwidth= -✓ BlockIOWriteBandwidth= -✓ TasksAccounting= -✓ TasksMax= -✓ Delegate= -✓ DisableControllers= -✓ IPAccounting= -✓ IPAddressAllow= -✓ IPAddressDeny= -✓ ManagedOOMSwap= -✓ ManagedOOMMemoryPressure= -✓ ManagedOOMMemoryPressureLimit= -✓ ManagedOOMMemoryPressureDurationSec= -✓ ManagedOOMPreference= -✓ CoredumpReceive= ``` ## Process Killing Settings @@ -292,13 +338,14 @@ All cgroup/resource control settings are available for transient units All process killing settings are available for transient units: ``` -✓ SendSIGKILL= -✓ SendSIGHUP= ✓ KillMode= +✓ SendSIGHUP= +✓ SendSIGKILL= ✓ KillSignal= ✓ RestartKillSignal= ✓ FinalKillSignal= ✓ WatchdogSignal= +✓ ReloadSignal= ``` ## Service Unit Settings @@ -306,41 +353,53 @@ All process killing settings are available for transient units: Most service unit settings are available for transient units. ``` -✓ BusName= -✓ ExecCondition= -✓ ExecReload= -✓ ExecStart= -✓ ExecStartPost= -✓ ExecStartPre= -✓ ExecStop= -✓ ExecStopPost= -✓ ExitType= -✓ FileDescriptorStoreMax= -✓ GuessMainPID= -✓ NonBlocking= -✓ NotifyAccess= -✓ OOMPolicy= ✓ PIDFile= -✓ RemainAfterExit= -✓ Restart= -✓ RestartForceExitStatus= -✓ RestartPreventExitStatus= -✓ RestartSec= -✓ RootDirectoryStartOnly= -✓ RuntimeMaxSec= -✓ RuntimeRandomizedExtraSec= - Sockets= -✓ SuccessExitStatus= -✓ TimeoutAbortSec= -✓ TimeoutSec= -✓ TimeoutStartFailureMode= -✓ TimeoutStartSec= -✓ TimeoutStopFailureMode= -✓ TimeoutStopSec= ✓ Type= +✓ ExitType= +✓ Restart= +✓ RestartMode= +✓ BusName= +✓ NotifyAccess= ✓ USBFunctionDescriptors= ✓ USBFunctionStrings= +✓ OOMPolicy= +✓ TimeoutStartFailureMode= +✓ TimeoutStopFailureMode= +✓ FileDescriptorStorePreserve= +✓ PermissionsStartOnly= +✓ RootDirectoryStartOnly= +✓ RemainAfterExit= +✓ GuessMainPID= +✓ RestartSec= +✓ RestartMaxDelaySec= +✓ TimeoutStartSec= +✓ TimeoutStopSec= +✓ TimeoutAbortSec= +✓ RuntimeMaxSec= +✓ RuntimeRandomizedExtraSec= ✓ WatchdogSec= +✓ TimeoutSec= +✓ FileDescriptorStoreMax= +✓ RestartSteps= +✓ ExecCondition= +✓ ExecStartPre= +✓ ExecStart= +✓ ExecStartPost= +✓ ExecConditionEx= +✓ ExecStartPreEx= +✓ ExecStartEx= +✓ ExecStartPostEx= +✓ ExecReload= +✓ ExecStop= +✓ ExecStopPost= +✓ ExecReloadEx= +✓ ExecStopEx= +✓ ExecStopPostEx= +✓ RestartPreventExitStatus= +✓ RestartForceExitStatus= +✓ SuccessExitStatus= +✓ OpenFile= + Socket= ``` ## Mount Unit Settings @@ -357,7 +416,7 @@ All mount unit settings are available to transient units: ✓ SloppyOptions= ✓ LazyUnmount= ✓ ForceUnmount= -✓ ReadWriteOnly= +✓ ReadwriteOnly= ``` ## Automount Unit Settings @@ -366,6 +425,7 @@ All automount unit setting is available to transient units: ``` ✓ Where= +✓ ExtraOptions= ✓ DirectoryMode= ✓ TimeoutIdleSec= ``` @@ -375,21 +435,21 @@ All automount unit setting is available to transient units: Most timer unit settings are available to transient units. ``` -✓ OnActiveSec= -✓ OnBootSec= -✓ OnCalendar= -✓ OnClockChange= -✓ OnStartupSec= -✓ OnTimezoneChange= -✓ OnUnitActiveSec= -✓ OnUnitInactiveSec= -✓ Persistent= ✓ WakeSystem= ✓ RemainAfterElapse= -✓ AccuracySec= -✓ RandomizedDelaySec= +✓ Persistent= +✓ OnTimezoneChange= +✓ OnClockChange= ✓ FixedRandomDelay= ✓ DeferReactivation= +✓ AccuracySec= +✓ RandomizedDelaySec= +✓ OnActiveSec= +✓ OnBootSec= +✓ OnStartupSec= +✓ OnUnitActiveSec= +✓ OnUnitInactiveSec= +✓ OnCalendar= Unit= ``` @@ -407,6 +467,9 @@ such). ✓ RuntimeMaxSec= ✓ RuntimeRandomizedExtraSec= ✓ TimeoutStopSec= +✓ User= +✓ Group= +✓ OOMPolicy= ``` ## Socket Unit Settings @@ -414,65 +477,71 @@ such). Most socket unit settings are available to transient units. ``` -✓ ListenStream= -✓ ListenDatagram= -✓ ListenSequentialPacket= -✓ ListenFIFO= -✓ ListenNetlink= -✓ ListenSpecial= -✓ ListenMessageQueue= -✓ ListenUSBFunction= -✓ SocketProtocol= -✓ BindIPv6Only= -✓ Backlog= -✓ BindToDevice= -✓ ExecStartPre= -✓ ExecStartPost= -✓ ExecStopPre= -✓ ExecStopPost= -✓ TimeoutSec= -✓ SocketUser= -✓ SocketGroup= -✓ SocketMode= -✓ DirectoryMode= ✓ Accept= ✓ FlushPending= ✓ Writable= -✓ MaxConnections= -✓ MaxConnectionsPerSource= ✓ KeepAlive= -✓ KeepAliveTimeSec= -✓ KeepAliveIntervalSec= -✓ KeepAliveProbes= -✓ DeferAcceptSec= ✓ NoDelay= -✓ Priority= -✓ ReceiveBuffer= -✓ SendBuffer= -✓ IPTOS= -✓ IPTTL= -✓ Mark= -✓ PipeSize= ✓ FreeBind= ✓ Transparent= ✓ Broadcast= ✓ PassCredentials= +✓ PassFileDescriptorsToExec= ✓ PassSecurity= ✓ PassPacketInfo= -✓ TCPCongestion= ✓ ReusePort= +✓ RemoveOnStop= +✓ SELinuxContextFromNet= +✓ Priority= +✓ IPTTL= +✓ Mark= +✓ IPTOS= +✓ Backlog= +✓ MaxConnections= +✓ MaxConnectionsPerSource= +✓ KeepAliveProbes= +✓ TriggerLimitBurst= +✓ PollLimitBurst= +✓ SocketMode= +✓ DirectoryMode= ✓ MessageQueueMaxMessages= ✓ MessageQueueMessageSize= -✓ RemoveOnStop= -✓ Symlinks= -✓ FileDescriptorName= - Service= +✓ TimeoutSec= +✓ KeepAliveTimeSec= +✓ KeepAliveIntervalSec= +✓ DeferAcceptSec= +✓ DeferTrigger= +✓ DeferTriggerMaxSec= ✓ TriggerLimitIntervalSec= -✓ TriggerLimitBurst= +✓ PollLimitIntervalSec= +✓ ReceiveBuffer= +✓ SendBuffer= +✓ PipeSize= +✓ ExecStartPre= +✓ ExecStartPost= +✓ ExecReload= +✓ ExecStopPost= ✓ SmackLabel= ✓ SmackLabelIPIn= ✓ SmackLabelIPOut= -✓ SELinuxContextFromNet= +✓ TCPCongestion= +✓ BindToDevice= +✓ BindIPv6Only= +✓ FileDescriptorName= +✓ SocketUser= +✓ SocketGroup= +✓ Timestamping= +✓ Symlinks= +✓ SocketProtocol= +✓ ListenStream= +✓ ListenDatagram= +✓ ListenSequentialPacket= +✓ ListenNetlink= +✓ ListenSpecial= +✓ ListenMessageQueue= +✓ ListenFIFO= +✓ ListenUSBFunction= + Service= ``` ## Swap Unit Settings @@ -491,14 +560,18 @@ Swap units are currently not available at all as transient units: Most path unit settings are available to transient units. ``` +✓ MakeDirectory= +✓ DirectoryMode= ✓ PathExists= ✓ PathExistsGlob= ✓ PathChanged= ✓ PathModified= ✓ DirectoryNotEmpty= +✓ TriggerLimitBurst= +✓ PollLimitBurst= +✓ TriggerLimitIntervalSec= +✓ PollLimitIntervalSec= Unit= -✓ MakeDirectory= -✓ DirectoryMode= ``` ## Install Section diff --git a/man/systemd-analyze.xml b/man/systemd-analyze.xml index 7bdf850c60..e1c575757b 100644 --- a/man/systemd-analyze.xml +++ b/man/systemd-analyze.xml @@ -210,6 +210,12 @@ OPTIONS chid + + systemd-analyze + OPTIONS + transient-settings + TYPE + @@ -1121,6 +1127,39 @@ LEGEND: M → sys_vendor (LENOVO) ┄ F → product_family (ThinkPad X1 Carbon G + + <command>systemd-analyze transient-settings <replaceable>TYPE</replaceable>...</command> + + Lists properties that can be set for various unit types via command line interfaces, in + particular + systemctl1 + set-property and / + options in + systemd-run1, + systemd-nspawn1, and + systemd-mount1. + Those assignments are possible for a subset of the properties that can be set in config files, see + systemd.unit5, + systemd.exec5, + systemd.resource-control5, + and the other unit-type-specific pages. The TYPE argument must be a unit + type ("service", "socket", …). The properties that apply to the specific types are listed. + + Note: D-Bus properties documented in + org.freedesktop.systemd15 + form a partially overlapping set with the lists generated by this command. Many D-Bus properties and + transient settings share the same names, but for example, LogRateLimitIntervalSec= + is described in + systemd.exec5 and + would be listed by this command, but the corresponding D-Bus property described in + systemd.exec5 is + LogRateLimitIntervalUSec. + + + This verb is intended primarily for programatic generation of shell completions. + + + diff --git a/shell-completion/bash/systemd-analyze b/shell-completion/bash/systemd-analyze index 41a2151e12..e4ecafeddc 100644 --- a/shell-completion/bash/systemd-analyze +++ b/shell-completion/bash/systemd-analyze @@ -81,6 +81,7 @@ _systemd_analyze() { [ARCHITECTURES]='architectures' [FDSTORE]='fdstore' [CAPABILITY]='capability' + [TRANSIENT_SETTINGS]='transient-settings' ) local CONFIGS='locale.conf systemd/bootchart.conf systemd/coredump.conf systemd/journald.conf @@ -225,6 +226,13 @@ _systemd_analyze() { if [[ $cur = -* ]]; then comps='--help --version --no-pager --json=off --json=pretty --json=short -m --mask' fi + + elif __contains_word "$verb" ${VERBS[TRANSIENT_SETTINGS]}; then + if [[ $cur = -* ]]; then + comps='--help --version --no-pager' + else + comps="$(systemctl --no-legend --no-pager -t help)" + fi fi COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) diff --git a/shell-completion/zsh/_systemd-analyze b/shell-completion/zsh/_systemd-analyze index 50542b3962..e03ae31afd 100644 --- a/shell-completion/zsh/_systemd-analyze +++ b/shell-completion/zsh/_systemd-analyze @@ -47,6 +47,13 @@ _describe 'plot options' _options } +(( $+functions[_systemd-analyze_transient-settings] )) || + _systemd-analyze_transient-settings() { + local -a _types + _types=( $(systemctl --no-pager --no-legend -t help) ) + _describe -t types 'unit types' _types + } + (( $+functions[_systemd-analyze_commands] )) || _systemd-analyze_commands(){ local -a _systemd_analyze_cmds @@ -74,6 +81,7 @@ JSON or table format' 'security:Analyze security settings of a service' 'inspect-elf:Parse and print ELF package metadata' 'has-tpm2:Report whether TPM2 support is available' + 'transient-settings:List transient settings for unit types' # log-level, log-target, service-watchdogs have been deprecated ) diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c index bb5855ecc6..8745db63f5 100644 --- a/src/analyze/analyze.c +++ b/src/analyze/analyze.c @@ -12,7 +12,6 @@ #include "sd-json.h" #include "alloc-util.h" -#include "analyze-verify-util.h" #include "analyze.h" #include "analyze-architectures.h" #include "analyze-blame.h" @@ -46,8 +45,10 @@ #include "analyze-unit-files.h" #include "analyze-unit-paths.h" #include "analyze-verify.h" +#include "analyze-verify-util.h" #include "build.h" #include "bus-error.h" +#include "bus-unit-util.h" #include "bus-util.h" #include "calendarspec.h" #include "dissect-image.h" @@ -66,6 +67,7 @@ #include "string-util.h" #include "strv.h" #include "time-util.h" +#include "unit-def.h" #include "unit-name.h" #include "verbs.h" @@ -159,6 +161,29 @@ void time_parsing_hint(const char *p, bool calendar, bool timestamp, bool timesp "Use 'systemd-analyze timespan \"%s\"' instead?", p); } +static int verb_transient_settings(int argc, char *argv[], void *userdata) { + assert(argc >= 2); + + pager_open(arg_pager_flags); + + bool first = true; + STRV_FOREACH(arg, strv_skip(argv, 1)) { + UnitType t; + + t = unit_type_from_string(*arg); + if (t < 0) + return log_error_errno(t, "Invalid unit type '%s'.", *arg); + + if (!first) + puts(""); + + bus_dump_transient_settings(t); + first = false; + } + + return 0; +} + static int help(int argc, char *argv[], void *userdata) { _cleanup_free_ char *link = NULL, *dot_link = NULL; int r; @@ -200,6 +225,7 @@ static int help(int argc, char *argv[], void *userdata) { " architectures [NAME...] List known architectures\n" " smbios11 List strings passed via SMBIOS Type #11\n" " chid List local CHIDs\n" + " transient-settings TYPE... List transient settings for unit TYPE\n" "\n%3$sExpression Evaluation:%4$s\n" " condition CONDITION... Evaluate conditions and asserts\n" " compare-versions VERSION1 [OP] VERSION2\n" @@ -639,46 +665,47 @@ static int run(int argc, char *argv[]) { _cleanup_(umount_and_freep) char *mounted_dir = NULL; static const Verb verbs[] = { - { "help", VERB_ANY, VERB_ANY, 0, help }, - { "time", VERB_ANY, 1, VERB_DEFAULT, verb_time }, - { "blame", VERB_ANY, 1, 0, verb_blame }, - { "critical-chain", VERB_ANY, VERB_ANY, 0, verb_critical_chain }, - { "plot", VERB_ANY, 1, 0, verb_plot }, - { "dot", VERB_ANY, VERB_ANY, 0, verb_dot }, + { "help", VERB_ANY, VERB_ANY, 0, help }, + { "time", VERB_ANY, 1, VERB_DEFAULT, verb_time }, + { "blame", VERB_ANY, 1, 0, verb_blame }, + { "critical-chain", VERB_ANY, VERB_ANY, 0, verb_critical_chain }, + { "plot", VERB_ANY, 1, 0, verb_plot }, + { "dot", VERB_ANY, VERB_ANY, 0, verb_dot }, /* ↓ The following seven verbs are deprecated, from here … ↓ */ - { "log-level", VERB_ANY, 2, 0, verb_log_control }, - { "log-target", VERB_ANY, 2, 0, verb_log_control }, - { "set-log-level", 2, 2, 0, verb_log_control }, - { "get-log-level", VERB_ANY, 1, 0, verb_log_control }, - { "set-log-target", 2, 2, 0, verb_log_control }, - { "get-log-target", VERB_ANY, 1, 0, verb_log_control }, - { "service-watchdogs", VERB_ANY, 2, 0, verb_service_watchdogs }, + { "log-level", VERB_ANY, 2, 0, verb_log_control }, + { "log-target", VERB_ANY, 2, 0, verb_log_control }, + { "set-log-level", 2, 2, 0, verb_log_control }, + { "get-log-level", VERB_ANY, 1, 0, verb_log_control }, + { "set-log-target", 2, 2, 0, verb_log_control }, + { "get-log-target", VERB_ANY, 1, 0, verb_log_control }, + { "service-watchdogs", VERB_ANY, 2, 0, verb_service_watchdogs }, /* ↑ … until here ↑ */ - { "dump", VERB_ANY, VERB_ANY, 0, verb_dump }, - { "cat-config", 2, VERB_ANY, 0, verb_cat_config }, - { "unit-files", VERB_ANY, VERB_ANY, 0, verb_unit_files }, - { "unit-paths", 1, 1, 0, verb_unit_paths }, - { "exit-status", VERB_ANY, VERB_ANY, 0, verb_exit_status }, - { "syscall-filter", VERB_ANY, VERB_ANY, 0, verb_syscall_filters }, - { "capability", VERB_ANY, VERB_ANY, 0, verb_capabilities }, - { "filesystems", VERB_ANY, VERB_ANY, 0, verb_filesystems }, - { "condition", VERB_ANY, VERB_ANY, 0, verb_condition }, - { "compare-versions", 3, 4, 0, verb_compare_versions }, - { "verify", 2, VERB_ANY, 0, verb_verify }, - { "calendar", 2, VERB_ANY, 0, verb_calendar }, - { "timestamp", 2, VERB_ANY, 0, verb_timestamp }, - { "timespan", 2, VERB_ANY, 0, verb_timespan }, - { "security", VERB_ANY, VERB_ANY, 0, verb_security }, - { "inspect-elf", 2, VERB_ANY, 0, verb_elf_inspection }, - { "malloc", VERB_ANY, VERB_ANY, 0, verb_malloc }, - { "fdstore", 2, VERB_ANY, 0, verb_fdstore }, - { "image-policy", 2, 2, 0, verb_image_policy }, - { "has-tpm2", VERB_ANY, 1, 0, verb_has_tpm2 }, - { "pcrs", VERB_ANY, VERB_ANY, 0, verb_pcrs }, - { "srk", VERB_ANY, 1, 0, verb_srk }, - { "architectures", VERB_ANY, VERB_ANY, 0, verb_architectures }, - { "smbios11", VERB_ANY, 1, 0, verb_smbios11 }, - { "chid", VERB_ANY, VERB_ANY, 0, verb_chid }, + { "dump", VERB_ANY, VERB_ANY, 0, verb_dump }, + { "cat-config", 2, VERB_ANY, 0, verb_cat_config }, + { "unit-files", VERB_ANY, VERB_ANY, 0, verb_unit_files }, + { "unit-paths", 1, 1, 0, verb_unit_paths }, + { "exit-status", VERB_ANY, VERB_ANY, 0, verb_exit_status }, + { "syscall-filter", VERB_ANY, VERB_ANY, 0, verb_syscall_filters }, + { "capability", VERB_ANY, VERB_ANY, 0, verb_capabilities }, + { "filesystems", VERB_ANY, VERB_ANY, 0, verb_filesystems }, + { "condition", VERB_ANY, VERB_ANY, 0, verb_condition }, + { "compare-versions", 3, 4, 0, verb_compare_versions }, + { "verify", 2, VERB_ANY, 0, verb_verify }, + { "calendar", 2, VERB_ANY, 0, verb_calendar }, + { "timestamp", 2, VERB_ANY, 0, verb_timestamp }, + { "timespan", 2, VERB_ANY, 0, verb_timespan }, + { "security", VERB_ANY, VERB_ANY, 0, verb_security }, + { "inspect-elf", 2, VERB_ANY, 0, verb_elf_inspection }, + { "malloc", VERB_ANY, VERB_ANY, 0, verb_malloc }, + { "fdstore", 2, VERB_ANY, 0, verb_fdstore }, + { "image-policy", 2, 2, 0, verb_image_policy }, + { "has-tpm2", VERB_ANY, 1, 0, verb_has_tpm2 }, + { "pcrs", VERB_ANY, VERB_ANY, 0, verb_pcrs }, + { "srk", VERB_ANY, 1, 0, verb_srk }, + { "architectures", VERB_ANY, VERB_ANY, 0, verb_architectures }, + { "smbios11", VERB_ANY, 1, 0, verb_smbios11 }, + { "chid", VERB_ANY, VERB_ANY, 0, verb_chid }, + { "transient-settings", 2, VERB_ANY, 0, verb_transient_settings }, {} }; diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c index 8073925b4a..54d845a87b 100644 --- a/src/basic/cgroup-util.c +++ b/src/basic/cgroup-util.c @@ -2048,34 +2048,38 @@ int cg_has_coredump_receive(const char *path) { } const uint64_t cgroup_io_limit_defaults[_CGROUP_IO_LIMIT_TYPE_MAX] = { - [CGROUP_IO_RBPS_MAX] = CGROUP_LIMIT_MAX, - [CGROUP_IO_WBPS_MAX] = CGROUP_LIMIT_MAX, - [CGROUP_IO_RIOPS_MAX] = CGROUP_LIMIT_MAX, - [CGROUP_IO_WIOPS_MAX] = CGROUP_LIMIT_MAX, + [CGROUP_IO_RBPS_MAX] = CGROUP_LIMIT_MAX, + [CGROUP_IO_WBPS_MAX] = CGROUP_LIMIT_MAX, + [CGROUP_IO_RIOPS_MAX] = CGROUP_LIMIT_MAX, + [CGROUP_IO_WIOPS_MAX] = CGROUP_LIMIT_MAX, }; static const char* const cgroup_io_limit_type_table[_CGROUP_IO_LIMIT_TYPE_MAX] = { - [CGROUP_IO_RBPS_MAX] = "IOReadBandwidthMax", - [CGROUP_IO_WBPS_MAX] = "IOWriteBandwidthMax", - [CGROUP_IO_RIOPS_MAX] = "IOReadIOPSMax", - [CGROUP_IO_WIOPS_MAX] = "IOWriteIOPSMax", + [CGROUP_IO_RBPS_MAX] = "IOReadBandwidthMax", + [CGROUP_IO_WBPS_MAX] = "IOWriteBandwidthMax", + [CGROUP_IO_RIOPS_MAX] = "IOReadIOPSMax", + [CGROUP_IO_WIOPS_MAX] = "IOWriteIOPSMax", }; DEFINE_STRING_TABLE_LOOKUP(cgroup_io_limit_type, CGroupIOLimitType); +void cgroup_io_limits_list(void) { + DUMP_STRING_TABLE(cgroup_io_limit_type, CGroupIOLimitType, _CGROUP_IO_LIMIT_TYPE_MAX); +} + static const char *const cgroup_controller_table[_CGROUP_CONTROLLER_MAX] = { - [CGROUP_CONTROLLER_CPU] = "cpu", - [CGROUP_CONTROLLER_CPUACCT] = "cpuacct", - [CGROUP_CONTROLLER_CPUSET] = "cpuset", - [CGROUP_CONTROLLER_IO] = "io", - [CGROUP_CONTROLLER_BLKIO] = "blkio", - [CGROUP_CONTROLLER_MEMORY] = "memory", - [CGROUP_CONTROLLER_DEVICES] = "devices", - [CGROUP_CONTROLLER_PIDS] = "pids", - [CGROUP_CONTROLLER_BPF_FIREWALL] = "bpf-firewall", - [CGROUP_CONTROLLER_BPF_DEVICES] = "bpf-devices", - [CGROUP_CONTROLLER_BPF_FOREIGN] = "bpf-foreign", - [CGROUP_CONTROLLER_BPF_SOCKET_BIND] = "bpf-socket-bind", + [CGROUP_CONTROLLER_CPU] = "cpu", + [CGROUP_CONTROLLER_CPUACCT] = "cpuacct", + [CGROUP_CONTROLLER_CPUSET] = "cpuset", + [CGROUP_CONTROLLER_IO] = "io", + [CGROUP_CONTROLLER_BLKIO] = "blkio", + [CGROUP_CONTROLLER_MEMORY] = "memory", + [CGROUP_CONTROLLER_DEVICES] = "devices", + [CGROUP_CONTROLLER_PIDS] = "pids", + [CGROUP_CONTROLLER_BPF_FIREWALL] = "bpf-firewall", + [CGROUP_CONTROLLER_BPF_DEVICES] = "bpf-devices", + [CGROUP_CONTROLLER_BPF_FOREIGN] = "bpf-foreign", + [CGROUP_CONTROLLER_BPF_SOCKET_BIND] = "bpf-socket-bind", [CGROUP_CONTROLLER_BPF_RESTRICT_NETWORK_INTERFACES] = "bpf-restrict-network-interfaces", }; diff --git a/src/basic/cgroup-util.h b/src/basic/cgroup-util.h index 2dea0ebdc4..ac0c7d8c41 100644 --- a/src/basic/cgroup-util.h +++ b/src/basic/cgroup-util.h @@ -97,6 +97,7 @@ extern const uint64_t cgroup_io_limit_defaults[_CGROUP_IO_LIMIT_TYPE_MAX]; const char* cgroup_io_limit_type_to_string(CGroupIOLimitType t) _const_; CGroupIOLimitType cgroup_io_limit_type_from_string(const char *s) _pure_; +void cgroup_io_limits_list(void); /* Special values for the io.bfq.weight attribute */ #define CGROUP_BFQ_WEIGHT_INVALID UINT64_MAX diff --git a/src/basic/macro.h b/src/basic/macro.h index caefa7c57c..3ddc5272b8 100644 --- a/src/basic/macro.h +++ b/src/basic/macro.h @@ -152,7 +152,7 @@ static inline unsigned long ALIGN_POWER2(unsigned long u) { } while (false) #define STRV_MAKE(...) ((char**) ((const char*[]) { __VA_ARGS__, NULL })) -#define STRV_MAKE_EMPTY ((char*[1]) { NULL }) +#define STRV_EMPTY ((char*[1]) { NULL }) #define STRV_MAKE_CONST(...) ((const char* const*) ((const char*[]) { __VA_ARGS__, NULL })) /* Pointers range from NULL to POINTER_MAX */ diff --git a/src/basic/rlimit-util.c b/src/basic/rlimit-util.c index 803eaa09b2..22fd86301c 100644 --- a/src/basic/rlimit-util.c +++ b/src/basic/rlimit-util.c @@ -343,6 +343,11 @@ static const char* const rlimit_table[_RLIMIT_MAX] = { DEFINE_STRING_TABLE_LOOKUP(rlimit, int); +void rlimits_list(const char *prefix) { + FOREACH_ELEMENT(field, rlimit_table) + printf("%s%s\n", strempty(prefix), *field); +} + int rlimit_from_string_harder(const char *s) { const char *suffix; diff --git a/src/basic/rlimit-util.h b/src/basic/rlimit-util.h index 52ec8bd320..8b600fe1d2 100644 --- a/src/basic/rlimit-util.h +++ b/src/basic/rlimit-util.h @@ -10,6 +10,7 @@ const char* rlimit_to_string(int i) _const_; int rlimit_from_string(const char *s) _pure_; int rlimit_from_string_harder(const char *s) _pure_; +void rlimits_list(const char *prefix); int setrlimit_closest(int resource, const struct rlimit *rlim); int setrlimit_closest_all(const struct rlimit * const *rlim, int *which_failed); diff --git a/src/basic/unit-def.c b/src/basic/unit-def.c index 8bc5f56532..f98cfd4ae1 100644 --- a/src/basic/unit-def.c +++ b/src/basic/unit-def.c @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include + #include "alloc-util.h" #include "bus-label.h" #include "glyph-util.h" @@ -331,6 +333,10 @@ static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = { DEFINE_STRING_TABLE_LOOKUP(unit_dependency, UnitDependency); +void unit_types_list(void) { + DUMP_STRING_TABLE(unit_dependency, UnitDependency, _UNIT_DEPENDENCY_MAX); +} + static const char* const notify_access_table[_NOTIFY_ACCESS_MAX] = { [NOTIFY_NONE] = "none", [NOTIFY_MAIN] = "main", diff --git a/src/basic/unit-def.h b/src/basic/unit-def.h index fd55950ea6..7664f2881a 100644 --- a/src/basic/unit-def.h +++ b/src/basic/unit-def.h @@ -304,6 +304,7 @@ const char* unit_dbus_interface_from_name(const char *name); const char* unit_type_to_string(UnitType i) _const_; UnitType unit_type_from_string(const char *s) _pure_; +void unit_types_list(void); const char* unit_load_state_to_string(UnitLoadState i) _const_; UnitLoadState unit_load_state_from_string(const char *s) _pure_; diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index cc0449dd96..c5e02a912f 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -71,7 +71,7 @@ int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u) { &u->job_path); } -static int warn_deprecated(const char *field, const char *eq) { +static int warn_deprecated(_unused_ sd_bus_message *m, const char *field, const char *eq) { log_warning("D-Bus property %s is deprecated, ignoring assignment: %s=%s", field, field, eq); return 1; } @@ -125,6 +125,7 @@ DEFINE_BUS_APPEND_PARSE("i", sched_policy_from_string); DEFINE_BUS_APPEND_PARSE("i", secure_bits_from_string); DEFINE_BUS_APPEND_PARSE("i", signal_from_string); DEFINE_BUS_APPEND_PARSE("i", parse_ip_protocol); +DEFINE_BUS_APPEND_PARSE("i", mpol_from_string); DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, ioprio_parse_priority); DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, parse_nice); DEFINE_BUS_APPEND_PARSE_PTR("i", int32_t, int, safe_atoi); @@ -148,7 +149,7 @@ static int bus_append_string(sd_bus_message *m, const char *field, const char *e return 1; } -static int bus_append_strv(sd_bus_message *m, const char *field, const char *eq, const char *separator, ExtractFlags flags) { +static int bus_append_strv_full(sd_bus_message *m, const char *field, const char *eq, ExtractFlags flags) { int r; assert(m); @@ -173,7 +174,7 @@ static int bus_append_strv(sd_bus_message *m, const char *field, const char *eq, for (const char *p = eq;;) { _cleanup_free_ char *word = NULL; - r = extract_first_word(&p, &word, separator, flags); + r = extract_first_word(&p, &word, /* separators= */ NULL, flags); if (r == -ENOMEM) return log_oom(); if (r < 0) @@ -201,6 +202,14 @@ static int bus_append_strv(sd_bus_message *m, const char *field, const char *eq, return 1; } +static int bus_append_strv(sd_bus_message *m, const char *field, const char *eq) { + return bus_append_strv_full(m, field, eq, EXTRACT_UNQUOTE); +} + +static int bus_append_strv_cunescape(sd_bus_message *m, const char *field, const char *eq) { + return bus_append_strv_full(m, field, eq, EXTRACT_UNQUOTE | EXTRACT_CUNESCAPE); +} + static int bus_append_byte_array(sd_bus_message *m, const char *field, const void *buf, size_t n) { int r; @@ -253,11 +262,15 @@ static int bus_append_parse_sec_rename(sd_bus_message *m, const char *field, con return 1; } -static int bus_append_parse_size(sd_bus_message *m, const char *field, const char *eq, uint64_t base) { +static int bus_append_parse_sec_rename_infinity(sd_bus_message *m, const char *field, const char *eq) { + return bus_append_parse_sec_rename(m, field, isempty(eq) ? "infinity" : eq); +} + +static int bus_append_parse_size(sd_bus_message *m, const char *field, const char *eq) { uint64_t v; int r; - r = parse_size(eq, base, &v); + r = parse_size(eq, /* base= */ 1024, &v); if (r < 0) return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq); @@ -268,6 +281,284 @@ static int bus_append_parse_size(sd_bus_message *m, const char *field, const cha return 1; } +static int bus_append_parse_permyriad(sd_bus_message *m, const char *field, const char *eq) { + int r; + + r = parse_permyriad(eq); + if (r < 0) + return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq); + + /* Pass around scaled to 2^32-1 == 100% */ + r = sd_bus_message_append(m, "(sv)", field, "u", UINT32_SCALE_FROM_PERMYRIAD(r)); + if (r < 0) + return bus_log_create_error(r); + + return 1; +} + +static int bus_append_parse_cpu_set(sd_bus_message *m, const char *field, const char *eq) { + _cleanup_(cpu_set_done) CPUSet cpuset = {}; + _cleanup_free_ uint8_t *array = NULL; + size_t allocated; + int r; + + r = parse_cpu_set(eq, &cpuset); + if (r < 0) + return log_error_errno(r, "Failed to parse %s value: %s", field, eq); + + r = cpu_set_to_dbus(&cpuset, &array, &allocated); + if (r < 0) + return log_error_errno(r, "Failed to serialize %s: %m", field); + + return bus_append_byte_array(m, field, array, allocated); +} + +static int bus_append_parse_delegate(sd_bus_message *m, const char *field, const char *eq) { + int r; + + r = parse_boolean(eq); + if (r < 0) + return bus_append_strv(m, "DelegateControllers", eq); + + r = sd_bus_message_append(m, "(sv)", "Delegate", "b", r); + if (r < 0) + return bus_log_create_error(r); + + return 1; +} + +static int bus_append_parse_resource_limit(sd_bus_message *m, const char *field, const char *eq) { + int r; + + if (isempty(eq) || streq(eq, "infinity")) { + uint64_t x = streq(eq, "infinity") ? CGROUP_LIMIT_MAX : + STR_IN_SET(field, + "DefaultMemoryLow", + "DefaultMemoryMin", + "MemoryLow", + "MemoryMin") ? CGROUP_LIMIT_MIN : CGROUP_LIMIT_MAX; + + r = sd_bus_message_append(m, "(sv)", field, "t", x); + if (r < 0) + return bus_log_create_error(r); + + return 1; + } + + r = parse_permyriad(eq); + if (r >= 0) { + char *n; + + /* When this is a percentage we'll convert this into a relative value in the range + * 0…UINT32_MAX and pass it in the MemoryLowScale property (and related ones). This + * way the physical memory size can be determined server-side. */ + + n = strjoina(field, "Scale"); + r = sd_bus_message_append(m, "(sv)", n, "u", UINT32_SCALE_FROM_PERMYRIAD(r)); + if (r < 0) + return bus_log_create_error(r); + + return 1; + } + + if (streq(field, "TasksMax")) + return bus_append_safe_atou64(m, field, eq); + + return bus_append_parse_size(m, field, eq); +} + +static int bus_append_parse_cpu_quota(sd_bus_message *m, const char *field, const char *eq) { + uint64_t x; + int r; + + if (isempty(eq)) + x = USEC_INFINITY; + else { + r = parse_permyriad_unbounded(eq); + if (r == 0) + return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "%s value too small.", field); + if (r < 0) + return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq); + x = r * USEC_PER_SEC / 10000U; + } + + r = sd_bus_message_append(m, "(sv)", "CPUQuotaPerSecUSec", "t", x); + if (r < 0) + return bus_log_create_error(r); + + return 1; +} + +static int bus_append_parse_device_allow(sd_bus_message *m, const char *field, const char *eq) { + int r; + + if (isempty(eq)) + r = sd_bus_message_append(m, "(sv)", field, "a(ss)", 0); + else { + _cleanup_free_ char *_path = NULL; + const char *path = eq, *rwm = NULL, *e; + + e = strchr(eq, ' '); + if (e) { + path = _path = strndup(eq, e - eq); + if (!path) + return log_oom(); + + rwm = e + 1; + } + + r = sd_bus_message_append(m, "(sv)", field, "a(ss)", 1, path, strempty(rwm)); + } + if (r < 0) + return bus_log_create_error(r); + + return 1; +} + +static int bus_try_append_parse_cgroup_io_limit(sd_bus_message *m, const char *field, const char *eq) { + int r; + + if (cgroup_io_limit_type_from_string(field) < 0) + return 0; + + if (isempty(eq)) + r = sd_bus_message_append(m, "(sv)", field, "a(st)", 0); + else { + const char *e = strchr(eq, ' '); + if (!e) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Failed to parse %s value %s.", + field, eq); + + const char *bandwidth = e + 1; + _cleanup_free_ char *path = strndup(eq, e - eq); + if (!path) + return log_oom(); + + uint64_t bytes; + if (streq(bandwidth, "infinity")) + bytes = CGROUP_LIMIT_MAX; + else { + r = parse_size(bandwidth, 1000, &bytes); + if (r < 0) + return log_error_errno(r, "Failed to parse byte value %s: %m", bandwidth); + } + + r = sd_bus_message_append(m, "(sv)", field, "a(st)", 1, path, bytes); + } + if (r < 0) + return bus_log_create_error(r); + + return 1; +} + +static int bus_append_parse_io_device_weight(sd_bus_message *m, const char *field, const char *eq) { + int r; + + if (isempty(eq)) + r = sd_bus_message_append(m, "(sv)", field, "a(st)", 0); + else { + const char *e = strchr(eq, ' '); + if (!e) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Failed to parse %s value %s.", + field, eq); + + const char *weight = e + 1; + _cleanup_free_ char *path = strndup(eq, e - eq); + if (!path) + return log_oom(); + + uint64_t u; + r = safe_atou64(weight, &u); + if (r < 0) + return log_error_errno(r, "Failed to parse %s value %s: %m", field, weight); + + r = sd_bus_message_append(m, "(sv)", field, "a(st)", 1, path, u); + } + if (r < 0) + return bus_log_create_error(r); + + return 1; +} + +static int bus_append_parse_io_device_latency(sd_bus_message *m, const char *field, const char *eq) { + const char *field_usec = "IODeviceLatencyTargetUSec"; + int r; + + if (isempty(eq)) + r = sd_bus_message_append(m, "(sv)", field_usec, "a(st)", 0); + else { + const char *e = strchr(eq, ' '); + if (!e) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Failed to parse %s value %s.", + field, eq); + + const char *target = e + 1; + _cleanup_free_ char *path = strndup(eq, e - eq); + if (!path) + return log_oom(); + + usec_t usec; + r = parse_sec(target, &usec); + if (r < 0) + return log_error_errno(r, "Failed to parse %s value %s: %m", field, target); + + r = sd_bus_message_append(m, "(sv)", field_usec, "a(st)", 1, path, usec); + } + if (r < 0) + return bus_log_create_error(r); + + return 1; +} + +static int bus_append_bpf_program(sd_bus_message *m, const char *field, const char *eq) { + int r; + + if (isempty(eq)) + r = sd_bus_message_append(m, "(sv)", field, "a(ss)", 0); + else { + _cleanup_free_ char *word = NULL; + + r = extract_first_word(&eq, &word, ":", 0); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) + return log_error_errno(r, "Failed to parse %s: %m", field); + + r = sd_bus_message_append(m, "(sv)", field, "a(ss)", 1, word, eq); + } + if (r < 0) + return bus_log_create_error(r); + + return 1; +} + +static int bus_append_socket_filter(sd_bus_message *m, const char *field, const char *eq) { + int r; + + if (isempty(eq)) + r = sd_bus_message_append(m, "(sv)", field, "a(iiqq)", 0); + else { + int32_t family, ip_protocol; + uint16_t nr_ports, port_min; + + r = parse_socket_bind_item(eq, &family, &ip_protocol, &nr_ports, &port_min); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) + return log_error_errno(r, "Failed to parse %s", field); + + r = sd_bus_message_append( + m, "(sv)", field, "a(iiqq)", 1, family, ip_protocol, nr_ports, port_min); + } + if (r < 0) + return bus_log_create_error(r); + + return 1; +} + static int bus_append_exec_command(sd_bus_message *m, const char *field, const char *eq) { bool explicit_path = false, done = false, ambient_hack = false; _cleanup_strv_free_ char **l = NULL, **ex_opts = NULL; @@ -485,6 +776,141 @@ static int bus_append_ip_address_access(sd_bus_message *m, int family, const uni return sd_bus_message_close_container(m); } +static int bus_append_parse_ip_address_filter(sd_bus_message *m, const char *field, const char *eq) { + union in_addr_union prefix = {}; + unsigned char prefixlen; + int family, r; + + if (isempty(eq)) { + r = sd_bus_message_append(m, "(sv)", field, "a(iayu)", 0); + if (r < 0) + return bus_log_create_error(r); + + return 1; + } + + r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'v', "a(iayu)"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'a', "(iayu)"); + if (r < 0) + return bus_log_create_error(r); + + if (streq(eq, "any")) { + /* "any" is a shortcut for 0.0.0.0/0 and ::/0 */ + + r = bus_append_ip_address_access(m, AF_INET, &prefix, 0); + if (r < 0) + return bus_log_create_error(r); + + r = bus_append_ip_address_access(m, AF_INET6, &prefix, 0); + if (r < 0) + return bus_log_create_error(r); + + } else if (is_localhost(eq)) { + /* "localhost" is a shortcut for 127.0.0.0/8 and ::1/128 */ + + prefix.in.s_addr = htobe32(0x7f000000); + r = bus_append_ip_address_access(m, AF_INET, &prefix, 8); + if (r < 0) + return bus_log_create_error(r); + + prefix.in6 = (struct in6_addr) IN6ADDR_LOOPBACK_INIT; + r = bus_append_ip_address_access(m, AF_INET6, &prefix, 128); + if (r < 0) + return r; + + } else if (streq(eq, "link-local")) { + /* "link-local" is a shortcut for 169.254.0.0/16 and fe80::/64 */ + + prefix.in.s_addr = htobe32((UINT32_C(169) << 24 | UINT32_C(254) << 16)); + r = bus_append_ip_address_access(m, AF_INET, &prefix, 16); + if (r < 0) + return bus_log_create_error(r); + + prefix.in6 = (struct in6_addr) { + .s6_addr32[0] = htobe32(0xfe800000) + }; + r = bus_append_ip_address_access(m, AF_INET6, &prefix, 64); + if (r < 0) + return bus_log_create_error(r); + + } else if (streq(eq, "multicast")) { + /* "multicast" is a shortcut for 224.0.0.0/4 and ff00::/8 */ + + prefix.in.s_addr = htobe32((UINT32_C(224) << 24)); + r = bus_append_ip_address_access(m, AF_INET, &prefix, 4); + if (r < 0) + return bus_log_create_error(r); + + prefix.in6 = (struct in6_addr) { + .s6_addr32[0] = htobe32(0xff000000) + }; + r = bus_append_ip_address_access(m, AF_INET6, &prefix, 8); + if (r < 0) + return bus_log_create_error(r); + + } else + for (;;) { + _cleanup_free_ char *word = NULL; + + r = extract_first_word(&eq, &word, NULL, 0); + if (r == 0) + break; + if (r == -ENOMEM) + return log_oom(); + if (r < 0) + return log_error_errno(r, "Failed to parse %s: %s", field, eq); + + r = in_addr_prefix_from_string_auto(word, &family, &prefix, &prefixlen); + if (r < 0) + return log_error_errno(r, "Failed to parse IP address prefix: %s", word); + + r = bus_append_ip_address_access(m, family, &prefix, prefixlen); + if (r < 0) + return bus_log_create_error(r); + } + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + return 1; +} + +#define bus_append_trivial_array(m, field, eq, types, ...) \ + ({ \ + int r; \ + \ + if (isempty(eq)) \ + r = sd_bus_message_append(m, "(sv)", field, types, 0); \ + else \ + r = sd_bus_message_append(m, "(sv)", field, types, 1, __VA_ARGS__); \ + r < 0 ? bus_log_create_error(r) : 1; \ + }) + +static int bus_append_ip_filter_path(sd_bus_message *m, const char *field, const char *eq) { + return bus_append_trivial_array(m, field, eq, + "as", eq); +} + static int bus_append_nft_set(sd_bus_message *m, const char *field, const char *eq) { int r; @@ -570,1744 +996,1104 @@ static int bus_append_nft_set(sd_bus_message *m, const char *field, const char * return 1; } -static int bus_append_cgroup_property(sd_bus_message *m, const char *field, const char *eq) { - int r; - - if (STR_IN_SET(field, "DevicePolicy", - "Slice", - "ManagedOOMSwap", - "ManagedOOMMemoryPressure", - "ManagedOOMPreference", - "MemoryPressureWatch", - "DelegateSubgroup")) - return bus_append_string(m, field, eq); - - if (STR_IN_SET(field, "ManagedOOMMemoryPressureLimit")) { - r = parse_permyriad(eq); - if (r < 0) - return log_error_errno(r, "Failed to parse %s value: %s", field, eq); - - /* Pass around scaled to 2^32-1 == 100% */ - r = sd_bus_message_append(m, "(sv)", field, "u", UINT32_SCALE_FROM_PERMYRIAD(r)); - if (r < 0) - return bus_log_create_error(r); - - return 1; - } - - if (STR_IN_SET(field, "MemoryAccounting", - "MemoryZSwapWriteback", - "IOAccounting", - "TasksAccounting", - "IPAccounting", - "CoredumpReceive")) - return bus_append_parse_boolean(m, field, eq); - - if (STR_IN_SET(field, "CPUWeight", - "StartupCPUWeight")) - return bus_append_cg_cpu_weight_parse(m, field, eq); - - if (STR_IN_SET(field, "IOWeight", - "StartupIOWeight")) - return bus_append_cg_weight_parse(m, field, eq); - - if (STR_IN_SET(field, "AllowedCPUs", - "StartupAllowedCPUs", - "AllowedMemoryNodes", - "StartupAllowedMemoryNodes")) { - - _cleanup_(cpu_set_done) CPUSet cpuset = {}; - _cleanup_free_ uint8_t *array = NULL; - size_t allocated; - - r = parse_cpu_set(eq, &cpuset); - if (r < 0) - return log_error_errno(r, "Failed to parse %s value: %s", field, eq); - - r = cpu_set_to_dbus(&cpuset, &array, &allocated); - if (r < 0) - return log_error_errno(r, "Failed to serialize CPUSet: %m"); - - return bus_append_byte_array(m, field, array, allocated); - } - - if (streq(field, "DisableControllers")) - return bus_append_strv(m, "DisableControllers", eq, /* separator= */ NULL, EXTRACT_UNQUOTE); - - if (streq(field, "Delegate")) { - r = parse_boolean(eq); - if (r < 0) - return bus_append_strv(m, "DelegateControllers", eq, /* separator= */ NULL, EXTRACT_UNQUOTE); - - r = sd_bus_message_append(m, "(sv)", "Delegate", "b", r); - if (r < 0) - return bus_log_create_error(r); - - return 1; - } - - if (STR_IN_SET(field, "MemoryMin", - "DefaultMemoryLow", - "DefaultMemoryMin", - "MemoryLow", - "MemoryHigh", - "MemoryMax", - "MemorySwapMax", - "MemoryZSwapMax", - "TasksMax")) { - - if (streq(eq, "infinity")) { - r = sd_bus_message_append(m, "(sv)", field, "t", CGROUP_LIMIT_MAX); - if (r < 0) - return bus_log_create_error(r); - return 1; - } else if (isempty(eq)) { - uint64_t empty_value = STR_IN_SET(field, - "DefaultMemoryLow", - "DefaultMemoryMin", - "MemoryLow", - "MemoryMin") ? - CGROUP_LIMIT_MIN : - CGROUP_LIMIT_MAX; - - r = sd_bus_message_append(m, "(sv)", field, "t", empty_value); - if (r < 0) - return bus_log_create_error(r); - return 1; - } - - r = parse_permyriad(eq); - if (r >= 0) { - char *n; - - /* When this is a percentage we'll convert this into a relative value in the range - * 0…UINT32_MAX and pass it in the MemoryLowScale property (and related ones). This - * way the physical memory size can be determined server-side. */ - - n = strjoina(field, "Scale"); - r = sd_bus_message_append(m, "(sv)", n, "u", UINT32_SCALE_FROM_PERMYRIAD(r)); - if (r < 0) - return bus_log_create_error(r); - - return 1; - } - - if (streq(field, "TasksMax")) - return bus_append_safe_atou64(m, field, eq); - - return bus_append_parse_size(m, field, eq, 1024); - } - - if (streq(field, "CPUQuota")) { - if (isempty(eq)) - r = sd_bus_message_append(m, "(sv)", "CPUQuotaPerSecUSec", "t", USEC_INFINITY); - else { - r = parse_permyriad_unbounded(eq); - if (r == 0) - return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "CPU quota too small."); - if (r < 0) - return log_error_errno(r, "CPU quota '%s' invalid.", eq); - - r = sd_bus_message_append(m, "(sv)", "CPUQuotaPerSecUSec", "t", (((uint64_t) r * USEC_PER_SEC) / 10000U)); - } - - if (r < 0) - return bus_log_create_error(r); - - return 1; - } - - if (streq(field, "CPUQuotaPeriodSec")) { - usec_t u = USEC_INFINITY; - - r = parse_sec_def_infinity(eq, &u); - if (r < 0) - return log_error_errno(r, "CPU quota period '%s' invalid.", eq); - - r = sd_bus_message_append(m, "(sv)", "CPUQuotaPeriodUSec", "t", u); - if (r < 0) - return bus_log_create_error(r); - - return 1; - } - - if (streq(field, "DeviceAllow")) { - if (isempty(eq)) - r = sd_bus_message_append(m, "(sv)", field, "a(ss)", 0); - else { - const char *path = eq, *rwm = NULL, *e; - - e = strchr(eq, ' '); - if (e) { - path = strndupa_safe(eq, e - eq); - rwm = e+1; - } - - r = sd_bus_message_append(m, "(sv)", field, "a(ss)", 1, path, strempty(rwm)); - } - - if (r < 0) - return bus_log_create_error(r); - - return 1; - } - - if (cgroup_io_limit_type_from_string(field) >= 0) { - - if (isempty(eq)) - r = sd_bus_message_append(m, "(sv)", field, "a(st)", 0); - else { - const char *path, *bandwidth, *e; - uint64_t bytes; - - e = strchr(eq, ' '); - if (!e) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Failed to parse %s value %s.", - field, eq); - - path = strndupa_safe(eq, e - eq); - bandwidth = e+1; - - if (streq(bandwidth, "infinity")) - bytes = CGROUP_LIMIT_MAX; - else { - r = parse_size(bandwidth, 1000, &bytes); - if (r < 0) - return log_error_errno(r, "Failed to parse byte value %s: %m", bandwidth); - } - - r = sd_bus_message_append(m, "(sv)", field, "a(st)", 1, path, bytes); - } - - if (r < 0) - return bus_log_create_error(r); - - return 1; - } - - if (streq(field, "IODeviceWeight")) { - if (isempty(eq)) - r = sd_bus_message_append(m, "(sv)", field, "a(st)", 0); - else { - const char *path, *weight, *e; - uint64_t u; - - e = strchr(eq, ' '); - if (!e) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Failed to parse %s value %s.", - field, eq); - - path = strndupa_safe(eq, e - eq); - weight = e+1; - - r = safe_atou64(weight, &u); - if (r < 0) - return log_error_errno(r, "Failed to parse %s value %s: %m", field, weight); - - r = sd_bus_message_append(m, "(sv)", field, "a(st)", 1, path, u); - } - - if (r < 0) - return bus_log_create_error(r); - - return 1; - } - - if (streq(field, "IODeviceLatencyTargetSec")) { - const char *field_usec = "IODeviceLatencyTargetUSec"; - - if (isempty(eq)) - r = sd_bus_message_append(m, "(sv)", field_usec, "a(st)", USEC_INFINITY); - else { - const char *path, *target, *e; - usec_t usec; - - e = strchr(eq, ' '); - if (!e) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Failed to parse %s value %s.", - field, eq); - - path = strndupa_safe(eq, e - eq); - target = e+1; - - r = parse_sec(target, &usec); - if (r < 0) - return log_error_errno(r, "Failed to parse %s value %s: %m", field, target); - - r = sd_bus_message_append(m, "(sv)", field_usec, "a(st)", 1, path, usec); - } - - if (r < 0) - return bus_log_create_error(r); - - return 1; - } - - if (STR_IN_SET(field, "IPAddressAllow", - "IPAddressDeny")) { - unsigned char prefixlen; - union in_addr_union prefix = {}; - int family; - - if (isempty(eq)) { - r = sd_bus_message_append(m, "(sv)", field, "a(iayu)", 0); - if (r < 0) - return bus_log_create_error(r); - - return 1; - } - - r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'v', "a(iayu)"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'a', "(iayu)"); - if (r < 0) - return bus_log_create_error(r); - - if (streq(eq, "any")) { - /* "any" is a shortcut for 0.0.0.0/0 and ::/0 */ - - r = bus_append_ip_address_access(m, AF_INET, &prefix, 0); - if (r < 0) - return bus_log_create_error(r); - - r = bus_append_ip_address_access(m, AF_INET6, &prefix, 0); - if (r < 0) - return bus_log_create_error(r); - - } else if (is_localhost(eq)) { - /* "localhost" is a shortcut for 127.0.0.0/8 and ::1/128 */ - - prefix.in.s_addr = htobe32(0x7f000000); - r = bus_append_ip_address_access(m, AF_INET, &prefix, 8); - if (r < 0) - return bus_log_create_error(r); - - prefix.in6 = (struct in6_addr) IN6ADDR_LOOPBACK_INIT; - r = bus_append_ip_address_access(m, AF_INET6, &prefix, 128); - if (r < 0) - return r; - - } else if (streq(eq, "link-local")) { - /* "link-local" is a shortcut for 169.254.0.0/16 and fe80::/64 */ - - prefix.in.s_addr = htobe32((UINT32_C(169) << 24 | UINT32_C(254) << 16)); - r = bus_append_ip_address_access(m, AF_INET, &prefix, 16); - if (r < 0) - return bus_log_create_error(r); - - prefix.in6 = (struct in6_addr) { - .s6_addr32[0] = htobe32(0xfe800000) - }; - r = bus_append_ip_address_access(m, AF_INET6, &prefix, 64); - if (r < 0) - return bus_log_create_error(r); - - } else if (streq(eq, "multicast")) { - /* "multicast" is a shortcut for 224.0.0.0/4 and ff00::/8 */ - - prefix.in.s_addr = htobe32((UINT32_C(224) << 24)); - r = bus_append_ip_address_access(m, AF_INET, &prefix, 4); - if (r < 0) - return bus_log_create_error(r); - - prefix.in6 = (struct in6_addr) { - .s6_addr32[0] = htobe32(0xff000000) - }; - r = bus_append_ip_address_access(m, AF_INET6, &prefix, 8); - if (r < 0) - return bus_log_create_error(r); - - } else { - for (;;) { - _cleanup_free_ char *word = NULL; - - r = extract_first_word(&eq, &word, NULL, 0); - if (r == 0) - break; - if (r == -ENOMEM) - return log_oom(); - if (r < 0) - return log_error_errno(r, "Failed to parse %s: %s", field, eq); - - r = in_addr_prefix_from_string_auto(word, &family, &prefix, &prefixlen); - if (r < 0) - return log_error_errno(r, "Failed to parse IP address prefix: %s", word); - - r = bus_append_ip_address_access(m, family, &prefix, prefixlen); - if (r < 0) - return bus_log_create_error(r); - } - } - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - return 1; - } - - if (STR_IN_SET(field, "IPIngressFilterPath", - "IPEgressFilterPath")) { - if (isempty(eq)) - r = sd_bus_message_append(m, "(sv)", field, "as", 0); - else - r = sd_bus_message_append(m, "(sv)", field, "as", 1, eq); - - if (r < 0) - return bus_log_create_error(r); - - return 1; - } - - if (streq(field, "BPFProgram")) { - if (isempty(eq)) - r = sd_bus_message_append(m, "(sv)", field, "a(ss)", 0); - else { - _cleanup_free_ char *word = NULL; - - r = extract_first_word(&eq, &word, ":", 0); - if (r == -ENOMEM) - return log_oom(); - if (r < 0) - return log_error_errno(r, "Failed to parse %s: %m", field); - - r = sd_bus_message_append(m, "(sv)", field, "a(ss)", 1, word, eq); - } - if (r < 0) - return bus_log_create_error(r); - - return 1; - } - - if (STR_IN_SET(field, "SocketBindAllow", - "SocketBindDeny")) { - if (isempty(eq)) - r = sd_bus_message_append(m, "(sv)", field, "a(iiqq)", 0); - else { - int32_t family, ip_protocol; - uint16_t nr_ports, port_min; - - r = parse_socket_bind_item(eq, &family, &ip_protocol, &nr_ports, &port_min); - if (r == -ENOMEM) - return log_oom(); - if (r < 0) - return log_error_errno(r, "Failed to parse %s", field); - - r = sd_bus_message_append( - m, "(sv)", field, "a(iiqq)", 1, family, ip_protocol, nr_ports, port_min); - } - if (r < 0) - return bus_log_create_error(r); - - return 1; - } - - if (streq(field, "MemoryPressureThresholdSec")) - return bus_append_parse_sec_rename(m, field, eq); - - if (streq(field, "NFTSet")) - return bus_append_nft_set(m, field, eq); - - if (streq(field, "ManagedOOMMemoryPressureDurationSec")) - /* While infinity is disallowed in unit file, infinity is allowed in D-Bus API which - * means use the default memory pressure duration from oomd.conf. */ - return bus_append_parse_sec_rename(m, field, isempty(eq) ? "infinity" : eq); - - if (STR_IN_SET(field, - "MemoryLimit", - "CPUShares", - "StartupCPUShares", - "BlockIOAccounting", - "BlockIOWeight", - "StartupBlockIOWeight", - "BlockIODeviceWeight", - "BlockIOReadBandwidth", - "BlockIOWriteBandwidth", - "CPUAccounting")) - return warn_deprecated(field, eq); - - return 0; +static int bus_append_environment_files(sd_bus_message *m, const char *field, const char *eq) { + return bus_append_trivial_array(m, "EnvironmentFiles", eq, + "a(sb)", + eq[0] == '-' ? eq + 1 : eq, + eq[0] == '-'); } -static int bus_append_automount_property(sd_bus_message *m, const char *field, const char *eq) { - if (STR_IN_SET(field, "Where", - "ExtraOptions")) - return bus_append_string(m, field, eq); - - if (streq(field, "DirectoryMode")) - return bus_append_parse_mode(m, field, eq); - - if (streq(field, "TimeoutIdleSec")) - return bus_append_parse_sec_rename(m, field, eq); - - return 0; -} - -static int bus_append_execute_property(sd_bus_message *m, const char *field, const char *eq) { - const char *suffix; +static int bus_append_set_credential(sd_bus_message *m, const char *field, const char *eq) { int r; - if (STR_IN_SET(field, "User", - "Group", - "UtmpIdentifier", - "UtmpMode", - "PAMName", - "TTYPath", - "WorkingDirectory", - "RootDirectory", - "SyslogIdentifier", - "ProtectSystem", - "ProtectHome", - "PrivateTmpEx", - "PrivateUsersEx", - "ProtectControlGroupsEx", - "SELinuxContext", - "RootImage", - "RootVerity", - "RuntimeDirectoryPreserve", - "Personality", - "KeyringMode", - "ProtectProc", - "ProcSubset", - "NetworkNamespacePath", - "IPCNamespacePath", - "LogNamespace", - "RootImagePolicy", - "MountImagePolicy", - "ExtensionImagePolicy", - "PrivatePIDs")) - return bus_append_string(m, field, eq); - - if (STR_IN_SET(field, "IgnoreSIGPIPE", - "TTYVHangup", - "TTYReset", - "TTYVTDisallocate", - "PrivateTmp", - "PrivateDevices", - "PrivateNetwork", - "PrivateUsers", - "PrivateMounts", - "PrivateIPC", - "NoNewPrivileges", - "SyslogLevelPrefix", - "MemoryDenyWriteExecute", - "RestrictRealtime", - "DynamicUser", - "RemoveIPC", - "ProtectKernelTunables", - "ProtectKernelModules", - "ProtectKernelLogs", - "ProtectClock", - "ProtectControlGroups", - "MountAPIVFS", - "BindLogSockets", - "CPUSchedulingResetOnFork", - "LockPersonality", - "ProtectHostname", - "MemoryKSM", - "RestrictSUIDSGID", - "RootEphemeral", - "SetLoginEnvironment")) - return bus_append_parse_boolean(m, field, eq); - - if (STR_IN_SET(field, "ReadWriteDirectories", - "ReadOnlyDirectories", - "InaccessibleDirectories", - "ReadWritePaths", - "ReadOnlyPaths", - "InaccessiblePaths", - "ExecPaths", - "NoExecPaths", - "ExecSearchPath", - "ExtensionDirectories", - "ConfigurationDirectory", - "SupplementaryGroups", - "SystemCallArchitectures")) - return bus_append_strv(m, field, eq, /* separator= */ NULL, EXTRACT_UNQUOTE); - - if (STR_IN_SET(field, "SyslogLevel", - "LogLevelMax")) - return bus_append_log_level_from_string(m, field, eq); - - if (streq(field, "SyslogFacility")) - return bus_append_log_facility_unshifted_from_string(m, field, eq); - - if (streq(field, "SecureBits")) - return bus_append_secure_bits_from_string(m, field, eq); - - if (streq(field, "CPUSchedulingPolicy")) - return bus_append_sched_policy_from_string(m, field, eq); - - if (STR_IN_SET(field, "CPUSchedulingPriority", - "OOMScoreAdjust")) - return bus_append_safe_atoi(m, field, eq); - - if (streq(field, "CoredumpFilter")) - return bus_append_coredump_filter_mask_from_string(m, field, eq); - - if (streq(field, "Nice")) - return bus_append_parse_nice(m, field, eq); - - if (streq(field, "SystemCallErrorNumber")) - return bus_append_seccomp_parse_errno_or_action(m, field, eq); - - if (streq(field, "IOSchedulingClass")) - return bus_append_ioprio_class_from_string(m, field, eq); - - if (streq(field, "IOSchedulingPriority")) - return bus_append_ioprio_parse_priority(m, field, eq); - - if (STR_IN_SET(field, "RuntimeDirectoryMode", - "StateDirectoryMode", - "CacheDirectoryMode", - "LogsDirectoryMode", - "ConfigurationDirectoryMode", - "UMask")) - return bus_append_parse_mode(m, field, eq); - - if (streq(field, "TimerSlackNSec")) - return bus_append_parse_nsec(m, field, eq); - - if (streq(field, "LogRateLimitIntervalSec")) - return bus_append_parse_sec_rename(m, field, eq); - - if (STR_IN_SET(field, "LogRateLimitBurst", - "TTYRows", - "TTYColumns")) - return bus_append_safe_atou(m, field, eq); - - if (streq(field, "MountFlags")) - return bus_append_mount_propagation_flag_from_string(m, field, eq); - - if (STR_IN_SET(field, "Environment", - "UnsetEnvironment", - "PassEnvironment")) - return bus_append_strv(m, field, eq, /* separator= */ NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE); - - if (streq(field, "EnvironmentFile")) { - if (isempty(eq)) - r = sd_bus_message_append(m, "(sv)", "EnvironmentFiles", "a(sb)", 0); - else - r = sd_bus_message_append(m, "(sv)", "EnvironmentFiles", "a(sb)", 1, - eq[0] == '-' ? eq + 1 : eq, - eq[0] == '-'); - if (r < 0) - return bus_log_create_error(r); - - return 1; - } - - if (STR_IN_SET(field, "SetCredential", "SetCredentialEncrypted")) { - r = sd_bus_message_open_container(m, 'r', "sv"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append_basic(m, 's', field); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'v', "a(say)"); - if (r < 0) - return bus_log_create_error(r); - - if (isempty(eq)) - r = sd_bus_message_append(m, "a(say)", 0); - else { - _cleanup_free_ char *word = NULL; - const char *p = eq; - - r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS); - if (r == -ENOMEM) - return log_oom(); - if (r < 0) - return log_error_errno(r, "Failed to parse %s= parameter: %s", field, eq); - if (r == 0 || !p) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Missing argument to %s=.", field); - - r = sd_bus_message_open_container(m, 'a', "(say)"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'r', "say"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append(m, "s", word); - if (r < 0) - return bus_log_create_error(r); - - if (streq(field, "SetCredentialEncrypted")) { - _cleanup_free_ void *decoded = NULL; - size_t decoded_size; - - r = unbase64mem(p, &decoded, &decoded_size); - if (r < 0) - return log_error_errno(r, "Failed to base64 decode encrypted credential: %m"); - - r = sd_bus_message_append_array(m, 'y', decoded, decoded_size); - } else { - _cleanup_free_ char *unescaped = NULL; - ssize_t l; - - l = cunescape(p, UNESCAPE_ACCEPT_NUL, &unescaped); - if (l < 0) - return log_error_errno(l, "Failed to unescape %s= value: %s", field, p); - - r = sd_bus_message_append_array(m, 'y', unescaped, l); - } - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - } - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - return 1; - } - - if (STR_IN_SET(field, "LoadCredential", "LoadCredentialEncrypted")) { - r = sd_bus_message_open_container(m, 'r', "sv"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append_basic(m, 's', field); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'v', "a(ss)"); - if (r < 0) - return bus_log_create_error(r); - - if (isempty(eq)) - r = sd_bus_message_append(m, "a(ss)", 0); - else { - _cleanup_free_ char *word = NULL; - const char *p = eq; - - r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS); - if (r == -ENOMEM) - return log_oom(); - if (r < 0) - return log_error_errno(r, "Failed to parse %s= parameter: %s", field, eq); - if (r == 0) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Missing argument to %s=.", field); - - if (isempty(p)) /* If only one field is specified, then this means "inherit from above" */ - p = eq; - - r = sd_bus_message_append(m, "a(ss)", 1, word, p); - } - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - return 1; - } - - if (streq(field, "ImportCredential")) { - if (isempty(eq)) - r = sd_bus_message_append(m, "(sv)", field, "as", 0); - else - r = sd_bus_message_append(m, "(sv)", field, "as", 1, eq); - if (r < 0) - return bus_log_create_error(r); - - return 1; - } - - if (streq(field, "ImportCredentialEx")) { - r = sd_bus_message_open_container(m, 'r', "sv"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append_basic(m, 's', field); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'v', "a(ss)"); - if (r < 0) - return bus_log_create_error(r); - - if (isempty(eq)) - r = sd_bus_message_append(m, "a(ss)", 0); - else { - _cleanup_free_ char *word = NULL; - const char *p = eq; - - r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS); - if (r == -ENOMEM) - return log_oom(); - if (r < 0) - return log_error_errno(r, "Failed to parse %s= parameter: %s", field, eq); - if (r == 0) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Missing argument to %s=.", field); - - r = sd_bus_message_append(m, "a(ss)", 1, word, p); - } - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - return 1; - } - - if (streq(field, "LogExtraFields")) { - r = sd_bus_message_open_container(m, 'r', "sv"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append_basic(m, 's', "LogExtraFields"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'v', "aay"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'a', "ay"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append_array(m, 'y', eq, strlen(eq)); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - return 1; - } - - if (streq(field, "LogFilterPatterns")) { - r = sd_bus_message_append(m, "(sv)", "LogFilterPatterns", "a(bs)", 1, - eq[0] != '~', - eq[0] != '~' ? eq : eq + 1); - if (r < 0) - return bus_log_create_error(r); - - return 1; - } - - if (STR_IN_SET(field, "StandardInput", - "StandardOutput", - "StandardError")) { - const char *n, *appended; - - if ((n = startswith(eq, "fd:"))) { - appended = strjoina(field, "FileDescriptorName"); - r = sd_bus_message_append(m, "(sv)", appended, "s", n); - } else if ((n = startswith(eq, "file:"))) { - appended = strjoina(field, "File"); - r = sd_bus_message_append(m, "(sv)", appended, "s", n); - } else if ((n = startswith(eq, "append:"))) { - appended = strjoina(field, "FileToAppend"); - r = sd_bus_message_append(m, "(sv)", appended, "s", n); - } else if ((n = startswith(eq, "truncate:"))) { - appended = strjoina(field, "FileToTruncate"); - r = sd_bus_message_append(m, "(sv)", appended, "s", n); - } else - r = sd_bus_message_append(m, "(sv)", field, "s", eq); - if (r < 0) - return bus_log_create_error(r); - - return 1; - } - - if (streq(field, "StandardInputText")) { - _cleanup_free_ char *unescaped = NULL; - ssize_t l; - - l = cunescape(eq, 0, &unescaped); - if (l < 0) - return log_error_errno(l, "Failed to unescape text '%s': %m", eq); - - if (!strextend(&unescaped, "\n")) + r = sd_bus_message_open_container(m, 'r', "sv"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_basic(m, 's', field); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'v', "a(say)"); + if (r < 0) + return bus_log_create_error(r); + + if (isempty(eq)) + r = sd_bus_message_append(m, "a(say)", 0); + else { + _cleanup_free_ char *word = NULL; + const char *p = eq; + + r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS); + if (r == -ENOMEM) return log_oom(); - - /* Note that we don't expand specifiers here, but that should be OK, as this is a - * programmatic interface anyway */ - - return bus_append_byte_array(m, field, unescaped, l + 1); - } - - if (streq(field, "StandardInputData")) { - _cleanup_free_ void *decoded = NULL; - size_t sz; - - r = unbase64mem(eq, &decoded, &sz); if (r < 0) - return log_error_errno(r, "Failed to decode base64 data '%s': %m", eq); + return log_error_errno(r, "Failed to parse %s= parameter: %s", field, eq); + if (r == 0 || !p) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Missing argument to %s=.", field); - return bus_append_byte_array(m, field, decoded, sz); - } - - if ((suffix = startswith(field, "Limit"))) { - int rl; - - rl = rlimit_from_string(suffix); - if (rl >= 0) { - const char *sn; - struct rlimit l; - - r = rlimit_parse(rl, eq, &l); - if (r < 0) - return log_error_errno(r, "Failed to parse resource limit: %s", eq); - - r = sd_bus_message_append(m, "(sv)", field, "t", (uint64_t) l.rlim_max); - if (r < 0) - return bus_log_create_error(r); - - sn = strjoina(field, "Soft"); - r = sd_bus_message_append(m, "(sv)", sn, "t", (uint64_t) l.rlim_cur); - if (r < 0) - return bus_log_create_error(r); - - return 1; - } - } - - if (STR_IN_SET(field, "AppArmorProfile", - "SmackProcessLabel")) { - int ignore = 0; - const char *s = eq; - - if (eq[0] == '-') { - ignore = 1; - s = eq + 1; - } - - r = sd_bus_message_append(m, "(sv)", field, "(bs)", ignore, s); + r = sd_bus_message_open_container(m, 'a', "(say)"); if (r < 0) return bus_log_create_error(r); - return 1; - } - - if (STR_IN_SET(field, "CapabilityBoundingSet", - "AmbientCapabilities")) { - uint64_t sum = 0; - bool invert = false; - const char *p = eq; - - if (*p == '~') { - invert = true; - p++; - } - - r = capability_set_from_string(p, &sum); - if (r < 0) - return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq); - - sum = invert ? ~sum : sum; - - r = sd_bus_message_append(m, "(sv)", field, "t", sum); + r = sd_bus_message_open_container(m, 'r', "say"); if (r < 0) return bus_log_create_error(r); - return 1; - } - - if (streq(field, "CPUAffinity")) { - _cleanup_(cpu_set_done) CPUSet cpuset = {}; - _cleanup_free_ uint8_t *array = NULL; - size_t allocated; - - if (eq && streq(eq, "numa")) { - r = sd_bus_message_append(m, "(sv)", "CPUAffinityFromNUMA", "b", true); - if (r < 0) - return bus_log_create_error(r); - return r; - } - - r = parse_cpu_set(eq, &cpuset); - if (r < 0) - return log_error_errno(r, "Failed to parse %s value: %s", field, eq); - - r = cpu_set_to_dbus(&cpuset, &array, &allocated); - if (r < 0) - return log_error_errno(r, "Failed to serialize CPUAffinity: %m"); - - return bus_append_byte_array(m, field, array, allocated); - } - - if (streq(field, "NUMAPolicy")) { - r = mpol_from_string(eq); - if (r < 0) - return log_error_errno(r, "Failed to parse %s value: %s", field, eq); - - r = sd_bus_message_append(m, "(sv)", field, "i", (int32_t) r); + r = sd_bus_message_append(m, "s", word); if (r < 0) return bus_log_create_error(r); - return 1; - } + if (streq(field, "SetCredentialEncrypted")) { + _cleanup_free_ void *decoded = NULL; + size_t decoded_size; - if (streq(field, "NUMAMask")) { - _cleanup_(cpu_set_done) CPUSet nodes = {}; - _cleanup_free_ uint8_t *array = NULL; - size_t allocated; - - if (eq && streq(eq, "all")) { - r = numa_mask_add_all(&nodes); + r = unbase64mem(p, &decoded, &decoded_size); if (r < 0) - return log_error_errno(r, "Failed to create NUMA mask representing \"all\" NUMA nodes: %m"); + return log_error_errno(r, "Failed to base64 decode encrypted credential: %m"); + + r = sd_bus_message_append_array(m, 'y', decoded, decoded_size); } else { - r = parse_cpu_set(eq, &nodes); - if (r < 0) - return log_error_errno(r, "Failed to parse %s value: %s", field, eq); + _cleanup_free_ char *unescaped = NULL; + ssize_t l; + + l = cunescape(p, UNESCAPE_ACCEPT_NUL, &unescaped); + if (l < 0) + return log_error_errno(l, "Failed to unescape %s= value: %s", field, p); + + r = sd_bus_message_append_array(m, 'y', unescaped, l); } - - r = cpu_set_to_dbus(&nodes, &array, &allocated); if (r < 0) - return log_error_errno(r, "Failed to serialize NUMAMask: %m"); + return bus_log_create_error(r); - return bus_append_byte_array(m, field, array, allocated); + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); } + if (r < 0) + return bus_log_create_error(r); - if (STR_IN_SET(field, "RestrictAddressFamilies", - "RestrictFileSystems", - "SystemCallFilter", - "SystemCallLog", - "RestrictNetworkInterfaces")) { - int allow_list = 1; + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + return 1; +} + +static int bus_append_load_credential(sd_bus_message *m, const char *field, const char *eq) { + int r; + + r = sd_bus_message_open_container(m, 'r', "sv"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_basic(m, 's', field); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'v', "a(ss)"); + if (r < 0) + return bus_log_create_error(r); + + if (isempty(eq)) + r = sd_bus_message_append(m, "a(ss)", 0); + else { + _cleanup_free_ char *word = NULL; const char *p = eq; - if (*p == '~') { - allow_list = 0; - p++; - } - - r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); + r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS); + if (r == -ENOMEM) + return log_oom(); if (r < 0) - return bus_log_create_error(r); + return log_error_errno(r, "Failed to parse %s= parameter: %s", field, eq); + if (r == 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Missing argument to %s=.", field); - r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field); - if (r < 0) - return bus_log_create_error(r); + if (isempty(p)) /* If only one field is specified, then this means "inherit from above" */ + p = eq; - r = sd_bus_message_open_container(m, 'v', "(bas)"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'r', "bas"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append_basic(m, 'b', &allow_list); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'a', "s"); - if (r < 0) - return bus_log_create_error(r); - - for (;;) { - _cleanup_free_ char *word = NULL; - - r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE); - if (r == 0) - break; - if (r == -ENOMEM) - return log_oom(); - if (r < 0) - return log_error_errno(r, "Invalid syntax: %s", eq); - - r = sd_bus_message_append_basic(m, 's', word); - if (r < 0) - return bus_log_create_error(r); - } - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - return 1; + r = sd_bus_message_append(m, "a(ss)", 1, word, p); } + if (r < 0) + return bus_log_create_error(r); - if (STR_IN_SET(field, "RestrictNamespaces", - "DelegateNamespaces")) { - bool invert = false; - unsigned long all = UPDATE_FLAG(NAMESPACE_FLAGS_ALL, CLONE_NEWUSER, !streq(field, "DelegateNamespaces")); - unsigned long flags; + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); - r = parse_boolean(eq); - if (r > 0) - /* RestrictNamespaces= value gets stored into a field with reverse semantics (the - * namespaces which are retained), so RestrictNamespaces=true means we retain no - * access to any namespaces and vice-versa. */ - flags = streq(field, "RestrictNamespaces") ? 0 : all; - else if (r == 0) - flags = streq(field, "RestrictNamespaces") ? all : 0; + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + return 1; +} + +static int bus_append_import_credential(sd_bus_message *m, const char *field, const char *eq) { + int r; + + if (isempty(eq)) + r = sd_bus_message_append(m, "(sv)", "ImportCredential", "as", 0); + else { + _cleanup_free_ char *word = NULL; + const char *p = eq; + + r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) + return log_error_errno(r, "Failed to parse %s= parameter: %s", field, eq); + if (r == 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Missing argument to %s=.", field); + + if (!p) + r = sd_bus_message_append(m, "(sv)", "ImportCredential", "as", 1, eq); else { - if (eq[0] == '~') { - invert = true; - eq++; - } - - r = namespace_flags_from_string(eq, &flags); - if (r < 0) - return log_error_errno(r, "Failed to parse %s value %s.", field, eq); - } - - if (invert) - flags = (~flags) & all; - - r = sd_bus_message_append(m, "(sv)", field, "t", (uint64_t) flags); - if (r < 0) - return bus_log_create_error(r); - - return 1; - } - - if (STR_IN_SET(field, "BindPaths", - "BindReadOnlyPaths")) { - const char *p = eq; - - r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'v', "a(ssbt)"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'a', "(ssbt)"); - if (r < 0) - return bus_log_create_error(r); - - for (;;) { - _cleanup_free_ char *source = NULL, *destination = NULL; - char *s = NULL, *d = NULL; - bool ignore_enoent = false; - uint64_t flags = MS_REC; - - r = extract_first_word(&p, &source, ":" WHITESPACE, EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS); - if (r < 0) - return log_error_errno(r, "Failed to parse argument: %m"); - if (r == 0) - break; - - s = source; - if (s[0] == '-') { - ignore_enoent = true; - s++; - } - - if (p && p[-1] == ':') { - r = extract_first_word(&p, &destination, ":" WHITESPACE, EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS); - if (r < 0) - return log_error_errno(r, "Failed to parse argument: %m"); - if (r == 0) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Missing argument after ':': %s", - eq); - - d = destination; - - if (p && p[-1] == ':') { - _cleanup_free_ char *options = NULL; - - r = extract_first_word(&p, &options, NULL, EXTRACT_UNQUOTE); - if (r < 0) - return log_error_errno(r, "Failed to parse argument: %m"); - - if (isempty(options) || streq(options, "rbind")) - flags = MS_REC; - else if (streq(options, "norbind")) - flags = 0; - else - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Unknown options: %s", - eq); - } - } else - d = s; - - r = sd_bus_message_append(m, "(ssbt)", s, d, ignore_enoent, flags); + /* We need to send ImportCredentialEx */ + r = sd_bus_message_open_container(m, 'r', "sv"); if (r < 0) return bus_log_create_error(r); - } - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - return 1; - } - - if (streq(field, "TemporaryFileSystem")) { - const char *p = eq; - - r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'v', "a(ss)"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'a', "(ss)"); - if (r < 0) - return bus_log_create_error(r); - - for (;;) { - _cleanup_free_ char *word = NULL, *path = NULL; - const char *w; - - r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE); - if (r < 0) - return log_error_errno(r, "Failed to parse argument: %m"); - if (r == 0) - break; - - w = word; - r = extract_first_word(&w, &path, ":", EXTRACT_DONT_COALESCE_SEPARATORS); - if (r < 0) - return log_error_errno(r, "Failed to parse argument: %m"); - if (r == 0) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Failed to parse argument: %s", - p); - - r = sd_bus_message_append(m, "(ss)", path, w); + r = sd_bus_message_append_basic(m, 's', "ImportCredentialEx"); if (r < 0) return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'v', "a(ss)"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append(m, "a(ss)", 1, word, p); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + } + } + if (r < 0) + return bus_log_create_error(r); + + return 1; +} + +static int bus_append_log_extra_fields(sd_bus_message *m, const char *field, const char *eq) { + int r; + + r = sd_bus_message_open_container(m, 'r', "sv"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_basic(m, 's', field); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'v', "aay"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'a', "ay"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_array(m, 'y', eq, strlen(eq)); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + return 1; +} + +static int bus_append_log_filter_patterns(sd_bus_message *m, const char *field, const char *eq) { + int r; + + r = sd_bus_message_append(m, "(sv)", "LogFilterPatterns", "a(bs)", 1, + eq[0] != '~', + eq[0] != '~' ? eq : eq + 1); + if (r < 0) + return bus_log_create_error(r); + + return 1; +} + +static int bus_append_standard_inputs(sd_bus_message *m, const char *field, const char *eq) { + const char *n, *appended; + int r; + + if ((n = startswith(eq, "fd:"))) { + appended = strjoina(field, "FileDescriptorName"); + r = sd_bus_message_append(m, "(sv)", appended, "s", n); + } else if ((n = startswith(eq, "file:"))) { + appended = strjoina(field, "File"); + r = sd_bus_message_append(m, "(sv)", appended, "s", n); + } else if ((n = startswith(eq, "append:"))) { + appended = strjoina(field, "FileToAppend"); + r = sd_bus_message_append(m, "(sv)", appended, "s", n); + } else if ((n = startswith(eq, "truncate:"))) { + appended = strjoina(field, "FileToTruncate"); + r = sd_bus_message_append(m, "(sv)", appended, "s", n); + } else + r = sd_bus_message_append(m, "(sv)", field, "s", eq); + if (r < 0) + return bus_log_create_error(r); + + return 1; +} + +static int bus_append_standard_input_text(sd_bus_message *m, const char *field, const char *eq) { + _cleanup_free_ char *unescaped = NULL; + ssize_t l; + + l = cunescape(eq, 0, &unescaped); + if (l < 0) + return log_error_errno(l, "Failed to unescape text '%s': %m", eq); + + if (!strextend(&unescaped, "\n")) + return log_oom(); + + /* Note that we don't expand specifiers here, but that should be OK, as this is a + * programmatic interface anyway */ + + return bus_append_byte_array(m, field, unescaped, l + 1); +} + +static int bus_append_standard_input_data(sd_bus_message *m, const char *field, const char *eq) { + _cleanup_free_ void *decoded = NULL; + size_t sz; + int r; + + r = unbase64mem(eq, &decoded, &sz); + if (r < 0) + return log_error_errno(r, "Failed to decode base64 data '%s': %m", eq); + + return bus_append_byte_array(m, field, decoded, sz); +} + +static int bus_try_append_resource_limit(sd_bus_message *m, const char *field, const char *eq) { + int r; + + const char *suffix = startswith(field, "Limit"); + if (!suffix) + return 0; + + int rl = rlimit_from_string(suffix); + if (rl < 0) + return log_error_errno(rl, "Unknown setting '%s'.", field); + + struct rlimit l; + r = rlimit_parse(rl, eq, &l); + if (r < 0) + return log_error_errno(r, "Failed to parse resource limit: %s", eq); + + r = sd_bus_message_append(m, "(sv)", field, "t", (uint64_t) l.rlim_max); + if (r < 0) + return bus_log_create_error(r); + + const char *sn = strjoina(field, "Soft"); + r = sd_bus_message_append(m, "(sv)", sn, "t", (uint64_t) l.rlim_cur); + if (r < 0) + return bus_log_create_error(r); + + return 1; +} + +static void dump_resource_limits(void) { + rlimits_list("Limit"); +} + +static int bus_append_string_with_ignore(sd_bus_message *m, const char *field, const char *eq) { + int ignore = 0; + const char *s = eq; + int r; + + if (eq[0] == '-') { + ignore = 1; + s = eq + 1; + } + + r = sd_bus_message_append(m, "(sv)", field, "(bs)", ignore, s); + if (r < 0) + return bus_log_create_error(r); + + return 1; +} + +static int bus_append_capabilities(sd_bus_message *m, const char *field, const char *eq) { + uint64_t sum = 0; + bool invert = false; + const char *p = eq; + int r; + + if (*p == '~') { + invert = true; + p++; + } + + r = capability_set_from_string(p, &sum); + if (r < 0) + return log_error_errno(r, "Failed to parse %s value %s: %m", field, eq); + + sum = invert ? ~sum : sum; + + r = sd_bus_message_append(m, "(sv)", field, "t", sum); + if (r < 0) + return bus_log_create_error(r); + + return 1; +} + +static int bus_append_cpu_affinity(sd_bus_message *m, const char *field, const char *eq) { + int r; + + if (streq_ptr(eq, "numa")) { + r = sd_bus_message_append(m, "(sv)", "CPUAffinityFromNUMA", "b", true); + if (r < 0) + return bus_log_create_error(r); + return r; + } + + return bus_append_parse_cpu_set(m, field, eq); +} + +static int bus_append_numa_mask(sd_bus_message *m, const char *field, const char *eq) { + _cleanup_(cpu_set_done) CPUSet nodes = {}; + _cleanup_free_ uint8_t *array = NULL; + size_t allocated; + int r; + + if (eq && streq(eq, "all")) { + r = numa_mask_add_all(&nodes); + if (r < 0) + return log_error_errno(r, "Failed to create NUMA mask representing \"all\" NUMA nodes: %m"); + } else { + r = parse_cpu_set(eq, &nodes); + if (r < 0) + return log_error_errno(r, "Failed to parse %s value: %s", field, eq); + } + + r = cpu_set_to_dbus(&nodes, &array, &allocated); + if (r < 0) + return log_error_errno(r, "Failed to serialize %s: %m", field); + + return bus_append_byte_array(m, field, array, allocated); +} + +static int bus_append_filter_list(sd_bus_message *m, const char *field, const char *eq) { + int allow_list = 1; + const char *p = eq; + int r; + + if (*p == '~') { + allow_list = 0; + p++; + } + + r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'v', "(bas)"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'r', "bas"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_basic(m, 'b', &allow_list); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'a', "s"); + if (r < 0) + return bus_log_create_error(r); + + for (;;) { + _cleanup_free_ char *word = NULL; + + r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE); + if (r == 0) + break; + if (r == -ENOMEM) + return log_oom(); + if (r < 0) + return log_error_errno(r, "Invalid syntax: %s", eq); + + r = sd_bus_message_append_basic(m, 's', word); + if (r < 0) + return bus_log_create_error(r); + } + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + return 1; +} + +static int bus_append_namespace_list(sd_bus_message *m, const char *field, const char *eq) { + bool invert = false; + unsigned long all = UPDATE_FLAG(NAMESPACE_FLAGS_ALL, CLONE_NEWUSER, !streq(field, "DelegateNamespaces")); + unsigned long flags; + int r; + + r = parse_boolean(eq); + if (r > 0) + /* RestrictNamespaces= value gets stored into a field with reverse semantics (the + * namespaces which are retained), so RestrictNamespaces=true means we retain no + * access to any namespaces and vice-versa. */ + flags = streq(field, "RestrictNamespaces") ? 0 : all; + else if (r == 0) + flags = streq(field, "RestrictNamespaces") ? all : 0; + else { + if (eq[0] == '~') { + invert = true; + eq++; } - r = sd_bus_message_close_container(m); + r = namespace_flags_from_string(eq, &flags); if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - return 1; + return log_error_errno(r, "Failed to parse %s value %s.", field, eq); } - if (streq(field, "RootHash")) { - _cleanup_free_ void *roothash_decoded = NULL; - size_t roothash_decoded_size = 0; + if (invert) + flags = (~flags) & all; - /* We have the path to a roothash to load and decode, eg: RootHash=/foo/bar.roothash */ - if (path_is_absolute(eq)) - return bus_append_string(m, "RootHashPath", eq); + r = sd_bus_message_append(m, "(sv)", field, "t", (uint64_t) flags); + if (r < 0) + return bus_log_create_error(r); - /* We have a roothash to decode, eg: RootHash=012345789abcdef */ - r = unhexmem(eq, &roothash_decoded, &roothash_decoded_size); - if (r < 0) - return log_error_errno(r, "Failed to decode RootHash= '%s': %m", eq); - if (roothash_decoded_size < sizeof(sd_id128_t)) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "RootHash= '%s' is too short.", eq); + return 1; +} - return bus_append_byte_array(m, field, roothash_decoded, roothash_decoded_size); - } +static int bus_append_bind_paths(sd_bus_message *m, const char *field, const char *eq) { + const char *p = eq; + int r; - if (streq(field, "RootHashSignature")) { - _cleanup_free_ void *roothash_sig_decoded = NULL; - char *value; - size_t roothash_sig_decoded_size = 0; + r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); + if (r < 0) + return bus_log_create_error(r); - /* We have the path to a roothash signature to load and decode, eg: RootHash=/foo/bar.roothash.p7s */ - if (path_is_absolute(eq)) - return bus_append_string(m, "RootHashSignaturePath", eq); + r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field); + if (r < 0) + return bus_log_create_error(r); - if (!(value = startswith(eq, "base64:"))) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to decode RootHashSignature= '%s', not a path but doesn't start with 'base64:'.", eq); + r = sd_bus_message_open_container(m, 'v', "a(ssbt)"); + if (r < 0) + return bus_log_create_error(r); - /* We have a roothash signature to decode, eg: RootHashSignature=base64:012345789abcdef */ - r = unbase64mem(value, &roothash_sig_decoded, &roothash_sig_decoded_size); - if (r < 0) - return log_error_errno(r, "Failed to decode RootHashSignature= '%s': %m", eq); + r = sd_bus_message_open_container(m, 'a', "(ssbt)"); + if (r < 0) + return bus_log_create_error(r); - return bus_append_byte_array(m, field, roothash_sig_decoded, roothash_sig_decoded_size); - } + for (;;) { + _cleanup_free_ char *source = NULL, *destination = NULL; + char *s = NULL, *d = NULL; + bool ignore_enoent = false; + uint64_t flags = MS_REC; - if (streq(field, "RootImageOptions")) { - _cleanup_strv_free_ char **l = NULL; - const char *p = eq; - - r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'v', "a(ss)"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'a', "(ss)"); - if (r < 0) - return bus_log_create_error(r); - - r = strv_split_colon_pairs(&l, p); + r = extract_first_word(&p, &source, ":" WHITESPACE, EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS); if (r < 0) return log_error_errno(r, "Failed to parse argument: %m"); + if (r == 0) + break; - STRV_FOREACH_PAIR(first, second, l) { - r = sd_bus_message_append(m, "(ss)", - !isempty(*second) ? *first : "root", - !isempty(*second) ? *second : *first); - if (r < 0) - return bus_log_create_error(r); + s = source; + if (s[0] == '-') { + ignore_enoent = true; + s++; } - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - return 1; - } - - if (streq(field, "MountImages")) { - const char *p = eq; - - r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'v', "a(ssba(ss))"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'a', "(ssba(ss))"); - if (r < 0) - return bus_log_create_error(r); - - for (;;) { - _cleanup_free_ char *first = NULL, *second = NULL, *tuple = NULL; - const char *q = NULL, *source = NULL; - bool permissive = false; - - r = extract_first_word(&p, &tuple, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE); - if (r < 0) - return log_error_errno(r, "Failed to parse MountImages= property: %s", eq); - if (r == 0) - break; - - q = tuple; - r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &first, &second); - if (r < 0) - return log_error_errno(r, "Failed to parse MountImages= property: %s", eq); - if (r == 0) - continue; - - source = first; - if (source[0] == '-') { - permissive = true; - source++; - } - - if (isempty(second)) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Missing argument after ':': %s", - eq); - - r = sd_bus_message_open_container(m, 'r', "ssba(ss)"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append(m, "ssb", source, second, permissive); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'a', "(ss)"); - if (r < 0) - return bus_log_create_error(r); - - for (;;) { - _cleanup_free_ char *partition = NULL, *mount_options = NULL; - - r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &partition, &mount_options); - if (r < 0) - return log_error_errno(r, "Failed to parse MountImages= property: %s", eq); - if (r == 0) - break; - /* Single set of options, applying to the root partition/single filesystem */ - if (r == 1) { - r = sd_bus_message_append(m, "(ss)", "root", partition); - if (r < 0) - return bus_log_create_error(r); - - break; - } - - r = sd_bus_message_append(m, "(ss)", partition, mount_options); - if (r < 0) - return bus_log_create_error(r); - } - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - } - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - return 1; - } - - if (streq(field, "ExtensionImages")) { - const char *p = eq; - - r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'v', "a(sba(ss))"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'a', "(sba(ss))"); - if (r < 0) - return bus_log_create_error(r); - - for (;;) { - _cleanup_free_ char *source = NULL, *tuple = NULL; - const char *q = NULL, *s = NULL; - bool permissive = false; - - r = extract_first_word(&p, &tuple, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE); - if (r < 0) - return log_error_errno(r, "Failed to parse ExtensionImages= property: %s", eq); - if (r == 0) - break; - - q = tuple; - r = extract_first_word(&q, &source, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS); - if (r < 0) - return log_error_errno(r, "Failed to parse ExtensionImages= property: %s", eq); - if (r == 0) - continue; - - s = source; - if (s[0] == '-') { - permissive = true; - s++; - } - - r = sd_bus_message_open_container(m, 'r', "sba(ss)"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append(m, "sb", s, permissive); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'a', "(ss)"); - if (r < 0) - return bus_log_create_error(r); - - for (;;) { - _cleanup_free_ char *partition = NULL, *mount_options = NULL; - - r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &partition, &mount_options); - if (r < 0) - return log_error_errno(r, "Failed to parse ExtensionImages= property: %s", eq); - if (r == 0) - break; - /* Single set of options, applying to the root partition/single filesystem */ - if (r == 1) { - r = sd_bus_message_append(m, "(ss)", "root", partition); - if (r < 0) - return bus_log_create_error(r); - - break; - } - - r = sd_bus_message_append(m, "(ss)", partition, mount_options); - if (r < 0) - return bus_log_create_error(r); - } - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - } - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - return 1; - } - - if (STR_IN_SET(field, "StateDirectory", "RuntimeDirectory", "CacheDirectory", "LogsDirectory")) { - _cleanup_strv_free_ char **symlinks = NULL, **symlinks_ro = NULL, **sources = NULL, **sources_ro = NULL; - const char *p = eq; - - /* Adding new directories is supported from both *DirectorySymlink methods and the - * older ones, so first parse the input, and if we are given a new-style src:dst - * tuple use the new method, else use the old one. */ - - for (;;) { - _cleanup_free_ char *tuple = NULL, *source = NULL, *dest = NULL, *flags = NULL; - - r = extract_first_word(&p, &tuple, NULL, EXTRACT_UNQUOTE); + if (p && p[-1] == ':') { + r = extract_first_word(&p, &destination, ":" WHITESPACE, EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS); if (r < 0) return log_error_errno(r, "Failed to parse argument: %m"); if (r == 0) - break; + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Missing argument after ':': %s", eq); - const char *t = tuple; - r = extract_many_words(&t, ":", EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS, &source, &dest, &flags); - if (r <= 0) - return log_error_errno(r ?: SYNTHETIC_ERRNO(EINVAL), "Failed to parse argument: %m"); + d = destination; - path_simplify(source); + if (p && p[-1] == ':') { + _cleanup_free_ char *options = NULL; - if (isempty(dest) && isempty(flags)) { - r = strv_consume(&sources, TAKE_PTR(source)); + r = extract_first_word(&p, &options, NULL, EXTRACT_UNQUOTE); if (r < 0) - return bus_log_create_error(r); - } else if (isempty(flags)) { - path_simplify(dest); - r = strv_consume_pair(&symlinks, TAKE_PTR(source), TAKE_PTR(dest)); - if (r < 0) - return log_oom(); - } else { - ExecDirectoryFlags exec_directory_flags = exec_directory_flags_from_string(flags); - if (exec_directory_flags < 0 || (exec_directory_flags & ~_EXEC_DIRECTORY_FLAGS_PUBLIC) != 0) - return log_error_errno(r, "Failed to parse flags: %s", flags); + return log_error_errno(r, "Failed to parse argument: %m"); - if (!isempty(dest)) { - path_simplify(dest); - r = strv_consume_pair(&symlinks_ro, TAKE_PTR(source), TAKE_PTR(dest)); - } else - r = strv_consume(&sources_ro, TAKE_PTR(source)); - if (r < 0) - return log_oom(); + if (isempty(options) || streq(options, "rbind")) + flags = MS_REC; + else if (streq(options, "norbind")) + flags = 0; + else + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Unknown options: %s", eq); } - } + } else + d = s; - if (!strv_isempty(sources)) { - r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'v', "as"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append_strv(m, sources); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - } - - /* For State and Runtime directories we support an optional destination parameter, which - * will be used to create a symlink to the source. But it is new so we cannot change the - * old DBUS signatures, so append a new message type. */ - if (!strv_isempty(symlinks) || !strv_isempty(symlinks_ro) || !strv_isempty(sources_ro)) { - const char *symlink_field; - - r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); - if (r < 0) - return bus_log_create_error(r); - - if (streq(field, "StateDirectory")) - symlink_field = "StateDirectorySymlink"; - else if (streq(field, "RuntimeDirectory")) - symlink_field = "RuntimeDirectorySymlink"; - else if (streq(field, "CacheDirectory")) - symlink_field = "CacheDirectorySymlink"; - else if (streq(field, "LogsDirectory")) - symlink_field = "LogsDirectorySymlink"; - else - assert_not_reached(); - - r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, symlink_field); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'v', "a(sst)"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'a', "(sst)"); - if (r < 0) - return bus_log_create_error(r); - - STRV_FOREACH_PAIR(source, destination, symlinks) { - r = sd_bus_message_append(m, "(sst)", *source, *destination, UINT64_C(0)); - if (r < 0) - return bus_log_create_error(r); - } - - STRV_FOREACH_PAIR(source, destination, symlinks_ro) { - r = sd_bus_message_append(m, "(sst)", *source, *destination, (uint64_t) EXEC_DIRECTORY_READ_ONLY); - if (r < 0) - return bus_log_create_error(r); - } - - STRV_FOREACH(source, sources_ro) { - r = sd_bus_message_append(m, "(sst)", *source, "", (uint64_t) EXEC_DIRECTORY_READ_ONLY); - if (r < 0) - return bus_log_create_error(r); - } - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - } - - return 1; + r = sd_bus_message_append(m, "(ssbt)", s, d, ignore_enoent, flags); + if (r < 0) + return bus_log_create_error(r); } - if (streq(field, "ProtectHostnameEx")) { + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + return 1; +} + +static int bus_append_temporary_file_system(sd_bus_message *m, const char *field, const char *eq) { + const char *p = eq; + int r; + + r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'v', "a(ss)"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'a', "(ss)"); + if (r < 0) + return bus_log_create_error(r); + + for (;;) { + _cleanup_free_ char *word = NULL, *path = NULL; + const char *w; + + r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE); + if (r < 0) + return log_error_errno(r, "Failed to parse argument: %m"); + if (r == 0) + break; + + w = word; + r = extract_first_word(&w, &path, ":", EXTRACT_DONT_COALESCE_SEPARATORS); + if (r < 0) + return log_error_errno(r, "Failed to parse argument: %m"); + if (r == 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Failed to parse argument: %s", p); + + r = sd_bus_message_append(m, "(ss)", path, w); + if (r < 0) + return bus_log_create_error(r); + } + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + return 1; +} + +static int bus_append_root_hash(sd_bus_message *m, const char *field, const char *eq) { + _cleanup_free_ void *roothash_decoded = NULL; + size_t roothash_decoded_size = 0; + int r; + + /* We have the path to a roothash to load and decode, eg: RootHash=/foo/bar.roothash */ + if (path_is_absolute(eq)) + return bus_append_string(m, "RootHashPath", eq); + + /* We have a roothash to decode, eg: RootHash=012345789abcdef */ + r = unhexmem(eq, &roothash_decoded, &roothash_decoded_size); + if (r < 0) + return log_error_errno(r, "Failed to decode RootHash= '%s': %m", eq); + if (roothash_decoded_size < sizeof(sd_id128_t)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "RootHash= '%s' is too short.", eq); + + return bus_append_byte_array(m, field, roothash_decoded, roothash_decoded_size); +} + +static int bus_append_root_hash_signature(sd_bus_message *m, const char *field, const char *eq) { + char *value; + _cleanup_free_ void *roothash_sig_decoded = NULL; + size_t roothash_sig_decoded_size = 0; + int r; + + /* We have the path to a roothash signature to load and decode, eg: RootHash=/foo/bar.roothash.p7s */ + if (path_is_absolute(eq)) + return bus_append_string(m, "RootHashSignaturePath", eq); + + if (!(value = startswith(eq, "base64:"))) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Failed to decode %s=%s: neither a path nor starts with 'base64:'.", + field, eq); + + /* We have a roothash signature to decode, eg: RootHashSignature=base64:012345789abcdef */ + r = unbase64mem(value, &roothash_sig_decoded, &roothash_sig_decoded_size); + if (r < 0) + return log_error_errno(r, "Failed to decode %s=%s: %m", field, eq); + + return bus_append_byte_array(m, field, roothash_sig_decoded, roothash_sig_decoded_size); +} + +static int bus_append_root_image_options(sd_bus_message *m, const char *field, const char *eq) { + _cleanup_strv_free_ char **l = NULL; + const char *p = eq; + int r; + + r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'v', "a(ss)"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'a', "(ss)"); + if (r < 0) + return bus_log_create_error(r); + + r = strv_split_colon_pairs(&l, p); + if (r < 0) + return log_error_errno(r, "Failed to parse argument: %m"); + + STRV_FOREACH_PAIR(first, second, l) { + r = sd_bus_message_append(m, "(ss)", + !isempty(*second) ? *first : "root", + !isempty(*second) ? *second : *first); + if (r < 0) + return bus_log_create_error(r); + } + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + return 1; +} + +static int bus_append_mount_images(sd_bus_message *m, const char *field, const char *eq) { + const char *p = eq; + int r; + + r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'v', "a(ssba(ss))"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'a', "(ssba(ss))"); + if (r < 0) + return bus_log_create_error(r); + + for (;;) { + _cleanup_free_ char *first = NULL, *second = NULL, *tuple = NULL; + const char *q = NULL, *source = NULL; + bool permissive = false; + + r = extract_first_word(&p, &tuple, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE); + if (r < 0) + return log_error_errno(r, "Failed to parse %s property: %s", field, eq); + if (r == 0) + break; + + q = tuple; + r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &first, &second); + if (r < 0) + return log_error_errno(r, "Failed to parse %s property: %s", field, eq); + if (r == 0) + continue; + + source = first; + if (source[0] == '-') { + permissive = true; + source++; + } + + if (isempty(second)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Missing argument after ':' for %s: %s", field, eq); + + r = sd_bus_message_open_container(m, 'r', "ssba(ss)"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append(m, "ssb", source, second, permissive); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'a', "(ss)"); + if (r < 0) + return bus_log_create_error(r); + + for (;;) { + _cleanup_free_ char *partition = NULL, *mount_options = NULL; + + r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &partition, &mount_options); + if (r < 0) + return log_error_errno(r, "Failed to parse %s property: %s", field, eq); + if (r == 0) + break; + /* Single set of options, applying to the root partition/single filesystem */ + if (r == 1) { + r = sd_bus_message_append(m, "(ss)", "root", partition); + if (r < 0) + return bus_log_create_error(r); + + break; + } + + r = sd_bus_message_append(m, "(ss)", partition, mount_options); + if (r < 0) + return bus_log_create_error(r); + } + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + } + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + return 1; +} + +static int bus_append_extension_images(sd_bus_message *m, const char *field, const char *eq) { + const char *p = eq; + int r; + + r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'v', "a(sba(ss))"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'a', "(sba(ss))"); + if (r < 0) + return bus_log_create_error(r); + + for (;;) { + _cleanup_free_ char *source = NULL, *tuple = NULL; + const char *q = NULL, *s = NULL; + bool permissive = false; + + r = extract_first_word(&p, &tuple, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE); + if (r < 0) + return log_error_errno(r, "Failed to parse %s property: %s", field, eq); + if (r == 0) + break; + + q = tuple; + r = extract_first_word(&q, &source, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS); + if (r < 0) + return log_error_errno(r, "Failed to parse %s property: %s", field, eq); + if (r == 0) + continue; + + s = source; + if (s[0] == '-') { + permissive = true; + s++; + } + + r = sd_bus_message_open_container(m, 'r', "sba(ss)"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append(m, "sb", s, permissive); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'a', "(ss)"); + if (r < 0) + return bus_log_create_error(r); + + for (;;) { + _cleanup_free_ char *partition = NULL, *mount_options = NULL; + + r = extract_many_words(&q, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS, &partition, &mount_options); + if (r < 0) + return log_error_errno(r, "Failed to parse %s property: %s", field, eq); + if (r == 0) + break; + /* Single set of options, applying to the root partition/single filesystem */ + if (r == 1) { + r = sd_bus_message_append(m, "(ss)", "root", partition); + if (r < 0) + return bus_log_create_error(r); + + break; + } + + r = sd_bus_message_append(m, "(ss)", partition, mount_options); + if (r < 0) + return bus_log_create_error(r); + } + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + } + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + return 1; +} + +static int bus_append_directory(sd_bus_message *m, const char *field, const char *eq) { + _cleanup_strv_free_ char **symlinks = NULL, **symlinks_ro = NULL, **sources = NULL, **sources_ro = NULL; + const char *p = eq; + int r; + + /* Adding new directories is supported from both *DirectorySymlink methods and the + * older ones, so first parse the input, and if we are given a new-style src:dst + * tuple use the new method, else use the old one. */ + + for (;;) { + _cleanup_free_ char *tuple = NULL, *source = NULL, *dest = NULL, *flags = NULL; + + r = extract_first_word(&p, &tuple, NULL, EXTRACT_UNQUOTE); + if (r < 0) + return log_error_errno(r, "Failed to parse argument: %m"); + if (r == 0) + break; + + const char *t = tuple; + r = extract_many_words(&t, ":", EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS, &source, &dest, &flags); + if (r <= 0) + return log_error_errno(r ?: SYNTHETIC_ERRNO(EINVAL), "Failed to parse argument: %m"); + + path_simplify(source); + + if (isempty(dest) && isempty(flags)) { + r = strv_consume(&sources, TAKE_PTR(source)); + if (r < 0) + return bus_log_create_error(r); + } else if (isempty(flags)) { + path_simplify(dest); + r = strv_consume_pair(&symlinks, TAKE_PTR(source), TAKE_PTR(dest)); + if (r < 0) + return log_oom(); + } else { + ExecDirectoryFlags exec_directory_flags = exec_directory_flags_from_string(flags); + if (exec_directory_flags < 0 || (exec_directory_flags & ~_EXEC_DIRECTORY_FLAGS_PUBLIC) != 0) + return log_error_errno(r, "Failed to parse flags: %s", flags); + + if (!isempty(dest)) { + path_simplify(dest); + r = strv_consume_pair(&symlinks_ro, TAKE_PTR(source), TAKE_PTR(dest)); + } else + r = strv_consume(&sources_ro, TAKE_PTR(source)); + if (r < 0) + return log_oom(); + } + } + + if (!strv_isempty(sources)) { + r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'v', "as"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_strv(m, sources); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + } + + /* For State and Runtime directories we support an optional destination parameter, which + * will be used to create a symlink to the source. But it is new so we cannot change the + * old DBUS signatures, so append a new message type. */ + if (!strv_isempty(symlinks) || !strv_isempty(symlinks_ro) || !strv_isempty(sources_ro)) { + const char *symlink_field; + + r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); + if (r < 0) + return bus_log_create_error(r); + + if (streq(field, "StateDirectory")) + symlink_field = "StateDirectorySymlink"; + else if (streq(field, "RuntimeDirectory")) + symlink_field = "RuntimeDirectorySymlink"; + else if (streq(field, "CacheDirectory")) + symlink_field = "CacheDirectorySymlink"; + else if (streq(field, "LogsDirectory")) + symlink_field = "LogsDirectorySymlink"; + else + assert_not_reached(); + + r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, symlink_field); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'v', "a(sst)"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'a', "(sst)"); + if (r < 0) + return bus_log_create_error(r); + + STRV_FOREACH_PAIR(source, destination, symlinks) { + r = sd_bus_message_append(m, "(sst)", *source, *destination, UINT64_C(0)); + if (r < 0) + return bus_log_create_error(r); + } + + STRV_FOREACH_PAIR(source, destination, symlinks_ro) { + r = sd_bus_message_append(m, "(sst)", *source, *destination, (uint64_t) EXEC_DIRECTORY_READ_ONLY); + if (r < 0) + return bus_log_create_error(r); + } + + STRV_FOREACH(source, sources_ro) { + r = sd_bus_message_append(m, "(sst)", *source, "", (uint64_t) EXEC_DIRECTORY_READ_ONLY); + if (r < 0) + return bus_log_create_error(r); + } + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + } + + return 1; +} + +static int bus_append_protect_hostname(sd_bus_message *m, const char *field, const char *eq) { + int r; + + /* The command-line field is called "ProtectHostname". We also accept "ProtectHostnameEx" as the + * field name for backward compatibility. We set ProtectHostame or ProtectHostnameEx. */ + + r = parse_boolean(eq); + if (r >= 0) + r = sd_bus_message_append(m, "(sv)", "ProtectHostname", "b", r); + else { const char *colon = strchr(eq, ':'); if (colon) { if (isempty(colon + 1)) @@ -2317,632 +2103,763 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con if (!p) return -ENOMEM; - r = sd_bus_message_append(m, "(sv)", field, "(ss)", p, colon + 1); + r = sd_bus_message_append(m, "(sv)", "ProtectHostnameEx", "(ss)", p, colon + 1); } else - r = sd_bus_message_append(m, "(sv)", field, "(ss)", eq, NULL); - if (r < 0) - return bus_log_create_error(r); - - return 1; + r = sd_bus_message_append(m, "(sv)", "ProtectHostnameEx", "(ss)", eq, NULL); } - return 0; + if (r < 0) + return bus_log_create_error(r); + + return 1; } -static int bus_append_kill_property(sd_bus_message *m, const char *field, const char *eq) { - if (streq(field, "KillMode")) - return bus_append_string(m, field, eq); - - if (STR_IN_SET(field, "SendSIGHUP", - "SendSIGKILL")) - return bus_append_parse_boolean(m, field, eq); - - if (STR_IN_SET(field, "KillSignal", - "RestartKillSignal", - "FinalKillSignal", - "WatchdogSignal", - "ReloadSignal")) - return bus_append_signal_from_string(m, field, eq); - - return 0; +static int bus_append_paths(sd_bus_message *m, const char *field, const char *eq) { + return bus_append_trivial_array(m, "Paths", eq, + "a(ss)", field, eq); } -static int bus_append_mount_property(sd_bus_message *m, const char *field, const char *eq) { - - if (STR_IN_SET(field, "What", - "Where", - "Options", - "Type")) - return bus_append_string(m, field, eq); - - if (streq(field, "TimeoutSec")) - return bus_append_parse_sec_rename(m, field, eq); - - if (streq(field, "DirectoryMode")) - return bus_append_parse_mode(m, field, eq); - - if (STR_IN_SET(field, "SloppyOptions", - "LazyUnmount", - "ForceUnmount", - "ReadwriteOnly")) - return bus_append_parse_boolean(m, field, eq); - - return 0; -} - -static int bus_append_path_property(sd_bus_message *m, const char *field, const char *eq) { +static int bus_append_exit_status(sd_bus_message *m, const char *field, const char *eq) { + _cleanup_free_ int *status = NULL, *signal = NULL; + size_t n_status = 0, n_signal = 0; int r; - if (streq(field, "MakeDirectory")) - return bus_append_parse_boolean(m, field, eq); + for (const char *p = eq;;) { + _cleanup_free_ char *word = NULL; - if (streq(field, "DirectoryMode")) - return bus_append_parse_mode(m, field, eq); - - if (STR_IN_SET(field, "PathExists", - "PathExistsGlob", - "PathChanged", - "PathModified", - "DirectoryNotEmpty")) { - if (isempty(eq)) - r = sd_bus_message_append(m, "(sv)", "Paths", "a(ss)", 0); - else - r = sd_bus_message_append(m, "(sv)", "Paths", "a(ss)", 1, field, eq); + r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE); + if (r == 0) + break; + if (r == -ENOMEM) + return log_oom(); if (r < 0) - return bus_log_create_error(r); + return log_error_errno(r, "Invalid syntax in %s: %s", field, eq); - return 1; + /* We need to call exit_status_from_string() first, because we want + * to parse numbers as exit statuses, not signals. */ + + r = exit_status_from_string(word); + if (r >= 0) { + assert(r >= 0 && r < 256); + + if (!GREEDY_REALLOC(status, n_status + 1)) + return log_oom(); + + status[n_status++] = r; + + } else if ((r = signal_from_string(word)) >= 0) { + if (!GREEDY_REALLOC(signal, n_signal + 1)) + return log_oom(); + + signal[n_signal++] = r; + + } else + /* original r from exit_status_to_string() */ + return log_error_errno(r, "Invalid status or signal %s in %s: %m", + word, field); } - if (STR_IN_SET(field, "TriggerLimitBurst", "PollLimitBurst")) - return bus_append_safe_atou(m, field, eq); + r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); + if (r < 0) + return bus_log_create_error(r); - if (STR_IN_SET(field, "TriggerLimitIntervalSec", "PollLimitIntervalSec")) - return bus_append_parse_sec_rename(m, field, eq); + r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field); + if (r < 0) + return bus_log_create_error(r); - return 0; + r = sd_bus_message_open_container(m, 'v', "(aiai)"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'r', "aiai"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_array(m, 'i', status, n_status * sizeof(int)); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_array(m, 'i', signal, n_signal * sizeof(int)); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + return 1; } -static int bus_append_scope_property(sd_bus_message *m, const char *field, const char *eq) { - if (streq(field, "RuntimeMaxSec")) - return bus_append_parse_sec_rename(m, field, eq); +static int bus_append_action_exit_status(sd_bus_message *m, const char *field, const char *eq) { + int r; - if (streq(field, "RuntimeRandomizedExtraSec")) - return bus_append_parse_sec_rename(m, field, eq); + if (isempty(eq)) + r = sd_bus_message_append(m, "(sv)", field, "i", -1); + else { + uint8_t u; - if (streq(field, "TimeoutStopSec")) - return bus_append_parse_sec_rename(m, field, eq); + r = safe_atou8(eq, &u); + if (r < 0) + return log_error_errno(r, "Failed to parse %s=%s", field, eq); + + r = sd_bus_message_append(m, "(sv)", field, "i", (int) u); + } + if (r < 0) + return bus_log_create_error(r); + + return 1; +} + +static int bus_append_listen(sd_bus_message *m, const char *field, const char *eq) { + const char *p = ASSERT_PTR(startswith(field, "Listen")); + + return bus_append_trivial_array(m, "Listen", eq, + "a(ss)", p, eq); +} + +static int bus_append_timers_monotonic(sd_bus_message *m, const char *field, const char *eq) { + int r; + + if (isempty(eq)) + r = sd_bus_message_append(m, "(sv)", "TimersMonotonic", "a(st)", 0); + else { + usec_t t; + r = parse_sec(eq, &t); + if (r < 0) + return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq); + + r = sd_bus_message_append(m, "(sv)", "TimersMonotonic", "a(st)", 1, field, t); + } + if (r < 0) + return bus_log_create_error(r); + + return 1; +} + +static int bus_append_timers_calendar(sd_bus_message *m, const char *field, const char *eq) { + return bus_append_trivial_array(m, "TimersCalendar", eq, + "a(ss)", field, eq); +} + +static int bus_append_timeout_sec(sd_bus_message *m, const char *field, const char *eq) { + int r; + + r = bus_append_parse_sec_rename(m, "TimeoutStartSec", eq); + if (r < 0) + return r; + + return bus_append_parse_sec_rename(m, "TimeoutStopSec", eq); +} + +static int bus_try_append_condition(sd_bus_message *m, const char *field, const char *eq) { + ConditionType t = condition_type_from_string(field); + bool is_condition = t >= 0; + + if (!is_condition) { + t = assert_type_from_string(field); + if (t < 0) + return 0; + } + + const char *p = eq; + + int trigger = p && *p == '|'; + if (trigger) + p++; + + int negate = p && *p == '!'; + if (negate) + p++; + + return bus_append_trivial_array(m, + is_condition ? "Conditions" : "Asserts", + eq, + "a(sbbs)", + field, trigger, negate, p); +} + +static void dump_conditions(void) { + condition_types_list(); + assert_types_list(); +} + +static int bus_try_append_unit_dependency(sd_bus_message *m, const char *field, const char *eq) { + if (unit_dependency_from_string(field) < 0) + return 0; + + return bus_append_strv(m, field, eq); +} + +typedef struct BusProperty { + const char *name; + int (*convert)(sd_bus_message *m, const char *field, const char *eq); + void (*dump)(void); +} BusProperty; + +static const BusProperty cgroup_properties[] = { + { "DevicePolicy", bus_append_string }, + { "Slice", bus_append_string }, + { "ManagedOOMSwap", bus_append_string }, + { "ManagedOOMMemoryPressure", bus_append_string }, + { "ManagedOOMPreference", bus_append_string }, + { "MemoryPressureWatch", bus_append_string }, + { "DelegateSubgroup", bus_append_string }, + { "ManagedOOMMemoryPressureLimit", bus_append_parse_permyriad }, + { "MemoryAccounting", bus_append_parse_boolean }, + { "MemoryZSwapWriteback", bus_append_parse_boolean }, + { "IOAccounting", bus_append_parse_boolean }, + { "TasksAccounting", bus_append_parse_boolean }, + { "IPAccounting", bus_append_parse_boolean }, + { "CoredumpReceive", bus_append_parse_boolean }, + { "CPUWeight", bus_append_cg_cpu_weight_parse }, + { "StartupCPUWeight", bus_append_cg_cpu_weight_parse }, + { "IOWeight", bus_append_cg_weight_parse }, + { "StartupIOWeight", bus_append_cg_weight_parse }, + { "AllowedCPUs", bus_append_parse_cpu_set }, + { "StartupAllowedCPUs", bus_append_parse_cpu_set }, + { "AllowedMemoryNodes", bus_append_parse_cpu_set }, + { "StartupAllowedMemoryNodes", bus_append_parse_cpu_set }, + { "DisableControllers", bus_append_strv }, + { "Delegate", bus_append_parse_delegate }, + { "MemoryMin", bus_append_parse_resource_limit }, + { "DefaultMemoryLow", bus_append_parse_resource_limit }, + { "DefaultMemoryMin", bus_append_parse_resource_limit }, + { "MemoryLow", bus_append_parse_resource_limit }, + { "MemoryHigh", bus_append_parse_resource_limit }, + { "MemoryMax", bus_append_parse_resource_limit }, + { "MemorySwapMax", bus_append_parse_resource_limit }, + { "MemoryZSwapMax", bus_append_parse_resource_limit }, + { "TasksMax", bus_append_parse_resource_limit }, + { "CPUQuota", bus_append_parse_cpu_quota }, + { "CPUQuotaPeriodSec", bus_append_parse_sec_rename_infinity }, + { "DeviceAllow", bus_append_parse_device_allow }, + { "IODeviceWeight", bus_append_parse_io_device_weight }, + { "IODeviceLatencyTargetSec", bus_append_parse_io_device_latency }, + { "IPAddressAllow", bus_append_parse_ip_address_filter }, + { "IPAddressDeny", bus_append_parse_ip_address_filter }, + { "IPIngressFilterPath", bus_append_ip_filter_path }, + { "IPEgressFilterPath", bus_append_ip_filter_path }, + { "BPFProgram", bus_append_bpf_program }, + { "SocketBindAllow", bus_append_socket_filter }, + { "SocketBindDeny", bus_append_socket_filter }, + { "MemoryPressureThresholdSec", bus_append_parse_sec_rename }, + { "NFTSet", bus_append_nft_set }, + + /* While infinity is disallowed in unit file, infinity is allowed in D-Bus API which + * means use the default memory pressure duration from oomd.conf. */ + { "ManagedOOMMemoryPressureDurationSec", bus_append_parse_sec_rename_infinity }, + + { "MemoryLimit", warn_deprecated }, + { "CPUShares", warn_deprecated }, + { "StartupCPUShares", warn_deprecated }, + { "BlockIOAccounting", warn_deprecated }, + { "BlockIOWeight", warn_deprecated }, + { "StartupBlockIOWeight", warn_deprecated }, + { "BlockIODeviceWeight", warn_deprecated }, + { "BlockIOReadBandwidth", warn_deprecated }, + { "BlockIOWriteBandwidth", warn_deprecated }, + { "CPUAccounting", warn_deprecated }, + + { NULL, bus_try_append_parse_cgroup_io_limit, cgroup_io_limits_list }, + {} +}; + +static const BusProperty automount_properties[] = { + { "Where", bus_append_string }, + { "ExtraOptions", bus_append_string }, + { "DirectoryMode", bus_append_parse_mode }, + { "TimeoutIdleSec", bus_append_parse_sec_rename }, + {} +}; + +static const BusProperty execute_properties[] = { + { "User", bus_append_string }, + { "Group", bus_append_string }, + { "UtmpIdentifier", bus_append_string }, + { "UtmpMode", bus_append_string }, + { "PAMName", bus_append_string }, + { "TTYPath", bus_append_string }, + { "WorkingDirectory", bus_append_string }, + { "RootDirectory", bus_append_string }, + { "SyslogIdentifier", bus_append_string }, + { "ProtectSystem", bus_append_string }, + { "ProtectHome", bus_append_string }, + { "PrivateTmpEx", bus_append_string }, + { "PrivateUsersEx", bus_append_string }, + { "ProtectControlGroupsEx", bus_append_string }, + { "SELinuxContext", bus_append_string }, + { "RootImage", bus_append_string }, + { "RootVerity", bus_append_string }, + { "RuntimeDirectoryPreserve", bus_append_string }, + { "Personality", bus_append_string }, + { "KeyringMode", bus_append_string }, + { "ProtectProc", bus_append_string }, + { "ProcSubset", bus_append_string }, + { "NetworkNamespacePath", bus_append_string }, + { "IPCNamespacePath", bus_append_string }, + { "LogNamespace", bus_append_string }, + { "RootImagePolicy", bus_append_string }, + { "MountImagePolicy", bus_append_string }, + { "ExtensionImagePolicy", bus_append_string }, + { "PrivatePIDs", bus_append_string }, + { "IgnoreSIGPIPE", bus_append_parse_boolean }, + { "TTYVHangup", bus_append_parse_boolean }, + { "TTYReset", bus_append_parse_boolean }, + { "TTYVTDisallocate", bus_append_parse_boolean }, + { "PrivateTmp", bus_append_parse_boolean }, + { "PrivateDevices", bus_append_parse_boolean }, + { "PrivateNetwork", bus_append_parse_boolean }, + { "PrivateUsers", bus_append_parse_boolean }, + { "PrivateMounts", bus_append_parse_boolean }, + { "PrivateIPC", bus_append_parse_boolean }, + { "NoNewPrivileges", bus_append_parse_boolean }, + { "SyslogLevelPrefix", bus_append_parse_boolean }, + { "MemoryDenyWriteExecute", bus_append_parse_boolean }, + { "RestrictRealtime", bus_append_parse_boolean }, + { "DynamicUser", bus_append_parse_boolean }, + { "RemoveIPC", bus_append_parse_boolean }, + { "ProtectKernelTunables", bus_append_parse_boolean }, + { "ProtectKernelModules", bus_append_parse_boolean }, + { "ProtectKernelLogs", bus_append_parse_boolean }, + { "ProtectClock", bus_append_parse_boolean }, + { "ProtectControlGroups", bus_append_parse_boolean }, + { "MountAPIVFS", bus_append_parse_boolean }, + { "BindLogSockets", bus_append_parse_boolean }, + { "CPUSchedulingResetOnFork", bus_append_parse_boolean }, + { "LockPersonality", bus_append_parse_boolean }, + { "MemoryKSM", bus_append_parse_boolean }, + { "RestrictSUIDSGID", bus_append_parse_boolean }, + { "RootEphemeral", bus_append_parse_boolean }, + { "SetLoginEnvironment", bus_append_parse_boolean }, + { "ReadWriteDirectories", bus_append_strv }, + { "ReadOnlyDirectories", bus_append_strv }, + { "InaccessibleDirectories", bus_append_strv }, + { "ReadWritePaths", bus_append_strv }, + { "ReadOnlyPaths", bus_append_strv }, + { "InaccessiblePaths", bus_append_strv }, + { "ExecPaths", bus_append_strv }, + { "NoExecPaths", bus_append_strv }, + { "ExecSearchPath", bus_append_strv }, + { "ExtensionDirectories", bus_append_strv }, + { "ConfigurationDirectory", bus_append_strv }, + { "SupplementaryGroups", bus_append_strv }, + { "SystemCallArchitectures", bus_append_strv }, + { "SyslogLevel", bus_append_log_level_from_string }, + { "LogLevelMax", bus_append_log_level_from_string }, + { "SyslogFacility", bus_append_log_facility_unshifted_from_string }, + { "SecureBits", bus_append_secure_bits_from_string }, + { "CPUSchedulingPolicy", bus_append_sched_policy_from_string }, + { "CPUSchedulingPriority", bus_append_safe_atoi }, + { "OOMScoreAdjust", bus_append_safe_atoi }, + { "CoredumpFilter", bus_append_coredump_filter_mask_from_string }, + { "Nice", bus_append_parse_nice }, + { "SystemCallErrorNumber", bus_append_seccomp_parse_errno_or_action }, + { "IOSchedulingClass", bus_append_ioprio_class_from_string }, + { "IOSchedulingPriority", bus_append_ioprio_parse_priority }, + { "RuntimeDirectoryMode", bus_append_parse_mode }, + { "StateDirectoryMode", bus_append_parse_mode }, + { "CacheDirectoryMode", bus_append_parse_mode }, + { "LogsDirectoryMode", bus_append_parse_mode }, + { "ConfigurationDirectoryMode", bus_append_parse_mode }, + { "UMask", bus_append_parse_mode }, + { "TimerSlackNSec", bus_append_parse_nsec }, + { "LogRateLimitIntervalSec", bus_append_parse_sec_rename }, + { "LogRateLimitBurst", bus_append_safe_atou }, + { "TTYRows", bus_append_safe_atou }, + { "TTYColumns", bus_append_safe_atou }, + { "MountFlags", bus_append_mount_propagation_flag_from_string }, + { "Environment", bus_append_strv_cunescape }, + { "UnsetEnvironment", bus_append_strv_cunescape }, + { "PassEnvironment", bus_append_strv_cunescape }, + { "EnvironmentFile", bus_append_environment_files }, + { "SetCredential", bus_append_set_credential }, + { "SetCredentialEncrypted", bus_append_set_credential }, + { "LoadCredential", bus_append_load_credential }, + { "LoadCredentialEncrypted", bus_append_load_credential }, + { "ImportCredential", bus_append_import_credential }, + { "ImportCredentialEx", bus_append_import_credential }, + { "LogExtraFields", bus_append_log_extra_fields }, + { "LogFilterPatterns", bus_append_log_filter_patterns }, + { "StandardInput", bus_append_standard_inputs }, + { "StandardOutput", bus_append_standard_inputs }, + { "StandardError", bus_append_standard_inputs }, + { "StandardInputText", bus_append_standard_input_text }, + { "StandardInputData", bus_append_standard_input_data }, + { "AppArmorProfile", bus_append_string_with_ignore }, + { "SmackProcessLabel", bus_append_string_with_ignore }, + { "CapabilityBoundingSet", bus_append_capabilities }, + { "AmbientCapabilities", bus_append_capabilities }, + { "CPUAffinity", bus_append_cpu_affinity }, + { "NUMAPolicy", bus_append_mpol_from_string }, + { "NUMAMask", bus_append_numa_mask }, + { "RestrictAddressFamilies", bus_append_filter_list }, + { "RestrictFileSystems", bus_append_filter_list }, + { "SystemCallFilter", bus_append_filter_list }, + { "SystemCallLog", bus_append_filter_list }, + { "RestrictNetworkInterfaces", bus_append_filter_list }, + { "RestrictNamespaces", bus_append_namespace_list }, + { "DelegateNamespaces", bus_append_namespace_list }, + { "BindPaths", bus_append_bind_paths }, + { "BindReadOnlyPaths", bus_append_bind_paths }, + { "TemporaryFileSystem", bus_append_temporary_file_system }, + { "RootHash", bus_append_root_hash }, + { "RootHashSignature", bus_append_root_hash_signature }, + { "RootImageOptions", bus_append_root_image_options }, + { "MountImages", bus_append_mount_images }, + { "ExtensionImages", bus_append_extension_images }, + { "StateDirectory", bus_append_directory }, + { "RuntimeDirectory", bus_append_directory }, + { "CacheDirectory", bus_append_directory }, + { "LogsDirectory", bus_append_directory }, + { "ProtectHostname", bus_append_protect_hostname }, + { "ProtectHostnameEx", bus_append_protect_hostname }, + + { NULL, bus_try_append_resource_limit, dump_resource_limits }, + {} +}; + +static const BusProperty kill_properties[] = { + { "KillMode", bus_append_string }, + { "SendSIGHUP", bus_append_parse_boolean }, + { "SendSIGKILL", bus_append_parse_boolean }, + { "KillSignal", bus_append_signal_from_string }, + { "RestartKillSignal", bus_append_signal_from_string }, + { "FinalKillSignal", bus_append_signal_from_string }, + { "WatchdogSignal", bus_append_signal_from_string }, + { "ReloadSignal", bus_append_signal_from_string }, + {} +}; + +static const BusProperty mount_properties[] = { + { "What", bus_append_string }, + { "Where", bus_append_string }, + { "Options", bus_append_string }, + { "Type", bus_append_string }, + { "TimeoutSec", bus_append_parse_sec_rename }, + { "DirectoryMode", bus_append_parse_mode }, + { "SloppyOptions", bus_append_parse_boolean }, + { "LazyUnmount", bus_append_parse_boolean }, + { "ForceUnmount", bus_append_parse_boolean }, + { "ReadwriteOnly", bus_append_parse_boolean }, + {} +}; + +static const BusProperty path_properties[] = { + { "MakeDirectory", bus_append_parse_boolean }, + { "DirectoryMode", bus_append_parse_mode }, + { "PathExists", bus_append_paths }, + { "PathExistsGlob", bus_append_paths }, + { "PathChanged", bus_append_paths }, + { "PathModified", bus_append_paths }, + { "DirectoryNotEmpty", bus_append_paths }, + { "TriggerLimitBurst", bus_append_safe_atou }, + { "PollLimitBurst", bus_append_safe_atou }, + { "TriggerLimitIntervalSec", bus_append_parse_sec_rename }, + { "PollLimitIntervalSec", bus_append_parse_sec_rename }, + {} +}; + +static const BusProperty scope_properties[] = { + { "RuntimeMaxSec", bus_append_parse_sec_rename }, + { "RuntimeRandomizedExtraSec", bus_append_parse_sec_rename }, + { "TimeoutStopSec", bus_append_parse_sec_rename }, + { "OOMPolicy", bus_append_string }, /* Scope units don't have execution context but we still want to allow setting these two, * so let's handle them separately. */ - if (STR_IN_SET(field, "User", "Group")) - return bus_append_string(m, field, eq); - - if (streq(field, "OOMPolicy")) - return bus_append_string(m, field, eq); - - return 0; -} - -static int bus_append_service_property(sd_bus_message *m, const char *field, const char *eq) { - int r; - - if (STR_IN_SET(field, "PIDFile", - "Type", - "ExitType", - "Restart", - "RestartMode", - "BusName", - "NotifyAccess", - "USBFunctionDescriptors", - "USBFunctionStrings", - "OOMPolicy", - "TimeoutStartFailureMode", - "TimeoutStopFailureMode", - "FileDescriptorStorePreserve")) - return bus_append_string(m, field, eq); - - if (STR_IN_SET(field, "PermissionsStartOnly", - "RootDirectoryStartOnly", - "RemainAfterExit", - "GuessMainPID")) - return bus_append_parse_boolean(m, field, eq); - - if (STR_IN_SET(field, "RestartSec", - "RestartMaxDelaySec", - "TimeoutStartSec", - "TimeoutStopSec", - "TimeoutAbortSec", - "RuntimeMaxSec", - "RuntimeRandomizedExtraSec", - "WatchdogSec")) - return bus_append_parse_sec_rename(m, field, eq); - - if (streq(field, "TimeoutSec")) { - r = bus_append_parse_sec_rename(m, "TimeoutStartSec", eq); - if (r < 0) - return r; - - return bus_append_parse_sec_rename(m, "TimeoutStopSec", eq); - } - - if (STR_IN_SET(field, "FileDescriptorStoreMax", - "RestartSteps")) - return bus_append_safe_atou(m, field, eq); - - if (STR_IN_SET(field, "ExecCondition", - "ExecStartPre", - "ExecStart", - "ExecStartPost", - "ExecConditionEx", - "ExecStartPreEx", - "ExecStartEx", - "ExecStartPostEx", - "ExecReload", - "ExecStop", - "ExecStopPost", - "ExecReloadEx", - "ExecStopEx", - "ExecStopPostEx")) - return bus_append_exec_command(m, field, eq); - - if (STR_IN_SET(field, "RestartPreventExitStatus", - "RestartForceExitStatus", - "SuccessExitStatus")) { - _cleanup_free_ int *status = NULL, *signal = NULL; - size_t n_status = 0, n_signal = 0; - const char *p; - - for (p = eq;;) { - _cleanup_free_ char *word = NULL; - - r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE); - if (r == 0) - break; - if (r == -ENOMEM) - return log_oom(); - if (r < 0) - return log_error_errno(r, "Invalid syntax in %s: %s", field, eq); - - /* We need to call exit_status_from_string() first, because we want - * to parse numbers as exit statuses, not signals. */ - - r = exit_status_from_string(word); - if (r >= 0) { - assert(r >= 0 && r < 256); - - if (!GREEDY_REALLOC(status, n_status + 1)) - return log_oom(); - - status[n_status++] = r; - - } else if ((r = signal_from_string(word)) >= 0) { - if (!GREEDY_REALLOC(signal, n_signal + 1)) - return log_oom(); - - signal[n_signal++] = r; - - } else - /* original r from exit_status_to_string() */ - return log_error_errno(r, "Invalid status or signal %s in %s: %m", - word, field); - } - - r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'v', "(aiai)"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_open_container(m, 'r', "aiai"); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append_array(m, 'i', status, n_status * sizeof(int)); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_append_array(m, 'i', signal, n_signal * sizeof(int)); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - r = sd_bus_message_close_container(m); - if (r < 0) - return bus_log_create_error(r); - - return 1; - } - - if (streq(field, "OpenFile")) - return bus_append_open_file(m, field, eq); - - return 0; -} - -static int bus_append_socket_property(sd_bus_message *m, const char *field, const char *eq) { - int r; - - if (STR_IN_SET(field, "Accept", - "FlushPending", - "Writable", - "KeepAlive", - "NoDelay", - "FreeBind", - "Transparent", - "Broadcast", - "PassCredentials", - "PassPIDFD", - "PassSecurity", - "PassPacketInfo", - "AcceptFileDescriptors", - "ReusePort", - "RemoveOnStop", - "PassFileDescriptorsToExec", - "SELinuxContextFromNet")) - return bus_append_parse_boolean(m, field, eq); - - if (STR_IN_SET(field, "Priority", - "IPTTL", - "Mark")) - return bus_append_safe_atoi(m, field, eq); - - if (streq(field, "IPTOS")) - return bus_append_ip_tos_from_string(m, field, eq); - - if (STR_IN_SET(field, "Backlog", - "MaxConnections", - "MaxConnectionsPerSource", - "KeepAliveProbes", - "TriggerLimitBurst", - "PollLimitBurst")) - return bus_append_safe_atou(m, field, eq); - - if (STR_IN_SET(field, "SocketMode", - "DirectoryMode")) - return bus_append_parse_mode(m, field, eq); - - if (STR_IN_SET(field, "MessageQueueMaxMessages", - "MessageQueueMessageSize")) - return bus_append_safe_atoi64(m, field, eq); - - if (STR_IN_SET(field, "TimeoutSec", - "KeepAliveTimeSec", - "KeepAliveIntervalSec", - "DeferAcceptSec", - "TriggerLimitIntervalSec", - "PollLimitIntervalSec", - "DeferTriggerMaxSec")) - return bus_append_parse_sec_rename(m, field, eq); - - if (STR_IN_SET(field, "ReceiveBuffer", - "SendBuffer", - "PipeSize")) - return bus_append_parse_size(m, field, eq, 1024); - - if (STR_IN_SET(field, "ExecStartPre", - "ExecStartPost", - "ExecReload", - "ExecStopPost")) - return bus_append_exec_command(m, field, eq); - - if (STR_IN_SET(field, "SmackLabel", - "SmackLabelIPIn", - "SmackLabelIPOut", - "TCPCongestion", - "BindToDevice", - "BindIPv6Only", - "FileDescriptorName", - "SocketUser", - "SocketGroup", - "Timestamping", - "DeferTrigger")) - return bus_append_string(m, field, eq); - - if (streq(field, "Symlinks")) - return bus_append_strv(m, field, eq, /* separator= */ NULL, EXTRACT_UNQUOTE); - - if (streq(field, "SocketProtocol")) - return bus_append_parse_ip_protocol(m, field, eq); - - if (STR_IN_SET(field, "ListenStream", - "ListenDatagram", - "ListenSequentialPacket", - "ListenNetlink", - "ListenSpecial", - "ListenMessageQueue", - "ListenFIFO", - "ListenUSBFunction")) { - if (isempty(eq)) - r = sd_bus_message_append(m, "(sv)", "Listen", "a(ss)", 0); - else - r = sd_bus_message_append(m, "(sv)", "Listen", "a(ss)", 1, field + STRLEN("Listen"), eq); - if (r < 0) - return bus_log_create_error(r); - - return 1; - } - - return 0; -} -static int bus_append_timer_property(sd_bus_message *m, const char *field, const char *eq) { - int r; - - if (STR_IN_SET(field, "WakeSystem", - "RemainAfterElapse", - "Persistent", - "OnTimezoneChange", - "OnClockChange", - "FixedRandomDelay", - "DeferReactivation")) - return bus_append_parse_boolean(m, field, eq); - - if (STR_IN_SET(field, "AccuracySec", - "RandomizedDelaySec", - "RandomizedOffsetSec")) - return bus_append_parse_sec_rename(m, field, eq); - - if (STR_IN_SET(field, "OnActiveSec", - "OnBootSec", - "OnStartupSec", - "OnUnitActiveSec", - "OnUnitInactiveSec")) { - if (isempty(eq)) - r = sd_bus_message_append(m, "(sv)", "TimersMonotonic", "a(st)", 0); - else { - usec_t t; - r = parse_sec(eq, &t); - if (r < 0) - return log_error_errno(r, "Failed to parse %s=%s: %m", field, eq); - - r = sd_bus_message_append(m, "(sv)", "TimersMonotonic", "a(st)", 1, field, t); - } - if (r < 0) - return bus_log_create_error(r); - - return 1; - } - - if (streq(field, "OnCalendar")) { - if (isempty(eq)) - r = sd_bus_message_append(m, "(sv)", "TimersCalendar", "a(ss)", 0); - else - r = sd_bus_message_append(m, "(sv)", "TimersCalendar", "a(ss)", 1, field, eq); - if (r < 0) - return bus_log_create_error(r); - - return 1; - } - - return 0; -} - -static int bus_append_unit_property(sd_bus_message *m, const char *field, const char *eq) { - ConditionType t = _CONDITION_TYPE_INVALID; - bool is_condition = false; - int r; - - if (STR_IN_SET(field, "Description", - "SourcePath", - "OnFailureJobMode", - "JobTimeoutAction", - "JobTimeoutRebootArgument", - "StartLimitAction", - "FailureAction", - "SuccessAction", - "RebootArgument", - "CollectMode")) - return bus_append_string(m, field, eq); - - if (STR_IN_SET(field, "StopWhenUnneeded", - "RefuseManualStart", - "RefuseManualStop", - "AllowIsolate", - "IgnoreOnIsolate", - "SurviveFinalKillSignal", - "DefaultDependencies")) - return bus_append_parse_boolean(m, field, eq); - - if (STR_IN_SET(field, "JobTimeoutSec", - "JobRunningTimeoutSec", - "StartLimitIntervalSec")) - return bus_append_parse_sec_rename(m, field, eq); - - if (streq(field, "StartLimitBurst")) - return bus_append_safe_atou(m, field, eq); - - if (STR_IN_SET(field, "SuccessActionExitStatus", - "FailureActionExitStatus")) { - if (isempty(eq)) - r = sd_bus_message_append(m, "(sv)", field, "i", -1); - else { - uint8_t u; - - r = safe_atou8(eq, &u); - if (r < 0) - return log_error_errno(r, "Failed to parse %s=%s", field, eq); - - r = sd_bus_message_append(m, "(sv)", field, "i", (int) u); - } - if (r < 0) - return bus_log_create_error(r); - - return 1; - } - - if (unit_dependency_from_string(field) >= 0 || - STR_IN_SET(field, "Documentation", - "RequiresMountsFor", - "WantsMountsFor", - "Markers")) - return bus_append_strv(m, field, eq, /* separator= */ NULL, EXTRACT_UNQUOTE); - - t = condition_type_from_string(field); - if (t >= 0) - is_condition = true; - else - t = assert_type_from_string(field); - if (t >= 0) { - if (isempty(eq)) - r = sd_bus_message_append(m, "(sv)", is_condition ? "Conditions" : "Asserts", "a(sbbs)", 0); - else { - const char *p = eq; - int trigger, negate; - - trigger = *p == '|'; - if (trigger) - p++; - - negate = *p == '!'; - if (negate) - p++; - - r = sd_bus_message_append(m, "(sv)", is_condition ? "Conditions" : "Asserts", "a(sbbs)", 1, - field, trigger, negate, p); - } - if (r < 0) - return bus_log_create_error(r); - - return 1; - } - - return 0; -} + { "User", bus_append_string }, + { "Group", bus_append_string }, + {} +}; + +static const BusProperty service_properties[] = { + { "PIDFile", bus_append_string }, + { "Type", bus_append_string }, + { "ExitType", bus_append_string }, + { "Restart", bus_append_string }, + { "RestartMode", bus_append_string }, + { "BusName", bus_append_string }, + { "NotifyAccess", bus_append_string }, + { "USBFunctionDescriptors", bus_append_string }, + { "USBFunctionStrings", bus_append_string }, + { "OOMPolicy", bus_append_string }, + { "TimeoutStartFailureMode", bus_append_string }, + { "TimeoutStopFailureMode", bus_append_string }, + { "FileDescriptorStorePreserve", bus_append_string }, + { "PermissionsStartOnly", bus_append_parse_boolean }, + { "RootDirectoryStartOnly", bus_append_parse_boolean }, + { "RemainAfterExit", bus_append_parse_boolean }, + { "GuessMainPID", bus_append_parse_boolean }, + { "RestartSec", bus_append_parse_sec_rename }, + { "RestartMaxDelaySec", bus_append_parse_sec_rename }, + { "TimeoutStartSec", bus_append_parse_sec_rename }, + { "TimeoutStopSec", bus_append_parse_sec_rename }, + { "TimeoutAbortSec", bus_append_parse_sec_rename }, + { "RuntimeMaxSec", bus_append_parse_sec_rename }, + { "RuntimeRandomizedExtraSec", bus_append_parse_sec_rename }, + { "WatchdogSec", bus_append_parse_sec_rename }, + { "TimeoutSec", bus_append_timeout_sec }, + { "FileDescriptorStoreMax", bus_append_safe_atou }, + { "RestartSteps", bus_append_safe_atou }, + { "ExecCondition", bus_append_exec_command }, + { "ExecStartPre", bus_append_exec_command }, + { "ExecStart", bus_append_exec_command }, + { "ExecStartPost", bus_append_exec_command }, + { "ExecConditionEx", bus_append_exec_command }, + { "ExecStartPreEx", bus_append_exec_command }, + { "ExecStartEx", bus_append_exec_command }, + { "ExecStartPostEx", bus_append_exec_command }, + { "ExecReload", bus_append_exec_command }, + { "ExecStop", bus_append_exec_command }, + { "ExecStopPost", bus_append_exec_command }, + { "ExecReloadEx", bus_append_exec_command }, + { "ExecStopEx", bus_append_exec_command }, + { "ExecStopPostEx", bus_append_exec_command }, + { "RestartPreventExitStatus", bus_append_exit_status }, + { "RestartForceExitStatus", bus_append_exit_status }, + { "SuccessExitStatus", bus_append_exit_status }, + { "OpenFile", bus_append_open_file }, + {} +}; + +static const BusProperty socket_properties[] = { + { "Accept", bus_append_parse_boolean }, + { "FlushPending", bus_append_parse_boolean }, + { "Writable", bus_append_parse_boolean }, + { "KeepAlive", bus_append_parse_boolean }, + { "NoDelay", bus_append_parse_boolean }, + { "FreeBind", bus_append_parse_boolean }, + { "Transparent", bus_append_parse_boolean }, + { "Broadcast", bus_append_parse_boolean }, + { "PassCredentials", bus_append_parse_boolean }, + { "PassFileDescriptorsToExec", bus_append_parse_boolean }, + { "PassSecurity", bus_append_parse_boolean }, + { "PassPacketInfo", bus_append_parse_boolean }, + { "ReusePort", bus_append_parse_boolean }, + { "RemoveOnStop", bus_append_parse_boolean }, + { "SELinuxContextFromNet", bus_append_parse_boolean }, + { "Priority", bus_append_safe_atoi }, + { "IPTTL", bus_append_safe_atoi }, + { "Mark", bus_append_safe_atoi }, + { "IPTOS", bus_append_ip_tos_from_string }, + { "Backlog", bus_append_safe_atou }, + { "MaxConnections", bus_append_safe_atou }, + { "MaxConnectionsPerSource", bus_append_safe_atou }, + { "KeepAliveProbes", bus_append_safe_atou }, + { "TriggerLimitBurst", bus_append_safe_atou }, + { "PollLimitBurst", bus_append_safe_atou }, + { "SocketMode", bus_append_parse_mode }, + { "DirectoryMode", bus_append_parse_mode }, + { "MessageQueueMaxMessages", bus_append_safe_atoi64 }, + { "MessageQueueMessageSize", bus_append_safe_atoi64 }, + { "TimeoutSec", bus_append_parse_sec_rename }, + { "KeepAliveTimeSec", bus_append_parse_sec_rename }, + { "KeepAliveIntervalSec", bus_append_parse_sec_rename }, + { "DeferAcceptSec", bus_append_parse_sec_rename }, + { "TriggerLimitIntervalSec", bus_append_parse_sec_rename }, + { "PollLimitIntervalSec", bus_append_parse_sec_rename }, + { "DeferTriggerMaxSec", bus_append_parse_sec_rename }, + { "ReceiveBuffer", bus_append_parse_size }, + { "SendBuffer", bus_append_parse_size }, + { "PipeSize", bus_append_parse_size }, + { "ExecStartPre", bus_append_exec_command }, + { "ExecStartPost", bus_append_exec_command }, + { "ExecReload", bus_append_exec_command }, + { "ExecStopPost", bus_append_exec_command }, + { "SmackLabel", bus_append_string }, + { "SmackLabelIPIn", bus_append_string }, + { "SmackLabelIPOut", bus_append_string }, + { "TCPCongestion", bus_append_string }, + { "BindToDevice", bus_append_string }, + { "BindIPv6Only", bus_append_string }, + { "FileDescriptorName", bus_append_string }, + { "SocketUser", bus_append_string }, + { "SocketGroup", bus_append_string }, + { "Timestamping", bus_append_string }, + { "DeferTrigger", bus_append_string }, + { "Symlinks", bus_append_strv }, + { "SocketProtocol", bus_append_parse_ip_protocol }, + { "ListenStream", bus_append_listen }, + { "ListenDatagram", bus_append_listen }, + { "ListenSequentialPacket", bus_append_listen }, + { "ListenNetlink", bus_append_listen }, + { "ListenSpecial", bus_append_listen }, + { "ListenMessageQueue", bus_append_listen }, + { "ListenFIFO", bus_append_listen }, + { "ListenUSBFunction", bus_append_listen }, + {} +}; + +static const BusProperty timer_properties[] = { + { "WakeSystem", bus_append_parse_boolean }, + { "RemainAfterElapse", bus_append_parse_boolean }, + { "Persistent", bus_append_parse_boolean }, + { "OnTimezoneChange", bus_append_parse_boolean }, + { "OnClockChange", bus_append_parse_boolean }, + { "FixedRandomDelay", bus_append_parse_boolean }, + { "DeferReactivation", bus_append_parse_boolean }, + { "AccuracySec", bus_append_parse_sec_rename }, + { "RandomizedDelaySec", bus_append_parse_sec_rename }, + { "RandomizedOffsetSec", bus_append_parse_sec_rename }, + { "OnActiveSec", bus_append_timers_monotonic }, + { "OnBootSec", bus_append_timers_monotonic }, + { "OnStartupSec", bus_append_timers_monotonic }, + { "OnUnitActiveSec", bus_append_timers_monotonic }, + { "OnUnitInactiveSec", bus_append_timers_monotonic }, + { "OnCalendar", bus_append_timers_calendar }, + {} +}; + +static const BusProperty unit_properties[] = { + { "Description", bus_append_string }, + { "SourcePath", bus_append_string }, + { "OnFailureJobMode", bus_append_string }, + { "JobTimeoutAction", bus_append_string }, + { "JobTimeoutRebootArgument", bus_append_string }, + { "StartLimitAction", bus_append_string }, + { "FailureAction", bus_append_string }, + { "SuccessAction", bus_append_string }, + { "RebootArgument", bus_append_string }, + { "CollectMode", bus_append_string }, + { "StopWhenUnneeded", bus_append_parse_boolean }, + { "RefuseManualStart", bus_append_parse_boolean }, + { "RefuseManualStop", bus_append_parse_boolean }, + { "AllowIsolate", bus_append_parse_boolean }, + { "IgnoreOnIsolate", bus_append_parse_boolean }, + { "SurviveFinalKillSignal", bus_append_parse_boolean }, + { "DefaultDependencies", bus_append_parse_boolean }, + { "JobTimeoutSec", bus_append_parse_sec_rename }, + { "JobRunningTimeoutSec", bus_append_parse_sec_rename }, + { "StartLimitIntervalSec", bus_append_parse_sec_rename }, + { "StartLimitBurst", bus_append_safe_atou }, + { "SuccessActionExitStatus", bus_append_action_exit_status }, + { "FailureActionExitStatus", bus_append_action_exit_status }, + { "Documentation", bus_append_strv }, + { "RequiresMountsFor", bus_append_strv }, + { "WantsMountsFor", bus_append_strv }, + { "Markers", bus_append_strv }, + + { NULL, bus_try_append_unit_dependency, unit_types_list }, + { NULL, bus_try_append_condition, dump_conditions }, + {} +}; + +static const BusProperty* service_unit_properties[] = { + cgroup_properties, + execute_properties, + kill_properties, + service_properties, + unit_properties, + NULL, +}; + +static const BusProperty* socket_unit_properties[] = { + cgroup_properties, + execute_properties, + kill_properties, + socket_properties, + unit_properties, + NULL, +}; + +static const BusProperty* timer_unit_properties[] = { + timer_properties, + unit_properties, + NULL, +}; + +static const BusProperty* path_unit_properties[] = { + path_properties, + unit_properties, + NULL, +}; + +static const BusProperty* slice_unit_properties[] = { + cgroup_properties, + unit_properties, + NULL, +}; + +static const BusProperty* scope_unit_properties[] = { + cgroup_properties, + kill_properties, + scope_properties, + unit_properties, + NULL, +}; + +static const BusProperty* mount_unit_properties[] = { + cgroup_properties, + execute_properties, + kill_properties, + mount_properties, + unit_properties, + NULL, +}; + +static const BusProperty* automount_unit_properties[] = { + automount_properties, + unit_properties, + NULL, +}; + +static const BusProperty* other_unit_properties[] = { + unit_properties, + NULL, +}; + +static const BusProperty** unit_type_properties[_UNIT_TYPE_MAX] = { + [UNIT_SERVICE] = service_unit_properties, + [UNIT_SOCKET] = socket_unit_properties, + [UNIT_TIMER] = timer_unit_properties, + [UNIT_PATH] = path_unit_properties, + [UNIT_SLICE] = slice_unit_properties, + [UNIT_SCOPE] = scope_unit_properties, + [UNIT_MOUNT] = mount_unit_properties, + [UNIT_AUTOMOUNT] = automount_unit_properties, + [UNIT_TARGET] = other_unit_properties, + [UNIT_DEVICE] = other_unit_properties, + [UNIT_SWAP] = other_unit_properties, +}; int bus_append_unit_property_assignment(sd_bus_message *m, UnitType t, const char *assignment) { - const char *eq, *field; + _cleanup_free_ char *field = NULL; + const char *eq; int r; assert(m); assert(assignment); + assert(t >= 0 && t < _UNIT_TYPE_MAX); eq = strchr(assignment, '='); if (!eq) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Not an assignment: %s", assignment); - field = strndupa_safe(assignment, eq - assignment); + + field = strndup(assignment, eq - assignment); + if (!field) + return log_oom(); eq++; - switch (t) { - case UNIT_SERVICE: - r = bus_append_cgroup_property(m, field, eq); - if (r != 0) - return r; - - r = bus_append_execute_property(m, field, eq); - if (r != 0) - return r; - - r = bus_append_kill_property(m, field, eq); - if (r != 0) - return r; - - r = bus_append_service_property(m, field, eq); - if (r != 0) - return r; - break; - - case UNIT_SOCKET: - r = bus_append_cgroup_property(m, field, eq); - if (r != 0) - return r; - - r = bus_append_execute_property(m, field, eq); - if (r != 0) - return r; - - r = bus_append_kill_property(m, field, eq); - if (r != 0) - return r; - - r = bus_append_socket_property(m, field, eq); - if (r != 0) - return r; - break; - - case UNIT_TIMER: - r = bus_append_timer_property(m, field, eq); - if (r != 0) - return r; - break; - - case UNIT_PATH: - r = bus_append_path_property(m, field, eq); - if (r != 0) - return r; - break; - - case UNIT_SLICE: - r = bus_append_cgroup_property(m, field, eq); - if (r != 0) - return r; - break; - - case UNIT_SCOPE: - r = bus_append_cgroup_property(m, field, eq); - if (r != 0) - return r; - - r = bus_append_kill_property(m, field, eq); - if (r != 0) - return r; - - r = bus_append_scope_property(m, field, eq); - if (r != 0) - return r; - break; - - case UNIT_MOUNT: - r = bus_append_cgroup_property(m, field, eq); - if (r != 0) - return r; - - r = bus_append_execute_property(m, field, eq); - if (r != 0) - return r; - - r = bus_append_kill_property(m, field, eq); - if (r != 0) - return r; - - r = bus_append_mount_property(m, field, eq); - if (r != 0) - return r; - - break; - - case UNIT_AUTOMOUNT: - r = bus_append_automount_property(m, field, eq); - if (r != 0) - return r; - - break; - - case UNIT_TARGET: - case UNIT_DEVICE: - case UNIT_SWAP: - break; - - default: - assert_not_reached(); - } - - r = bus_append_unit_property(m, field, eq); - if (r != 0) - return r; + for (const BusProperty** tables = ASSERT_PTR(unit_type_properties[t]); *tables; tables++) + for (const BusProperty *item = *tables; item->convert; item++) + if (item->name) { + if (streq(item->name, field)) + return item->convert(m, field, eq); + } else { + /* If .name is not set, the function must be a "try" helper */ + r = item->convert(m, field, eq); + if (r != 0) + return r; + } return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown assignment: %s", assignment); @@ -2962,6 +2879,24 @@ int bus_append_unit_property_assignment_many(sd_bus_message *m, UnitType t, char return 0; } +void bus_dump_transient_settings(UnitType t) { + assert(t >= 0 && t < _UNIT_TYPE_MAX); + + for (const BusProperty** tables = ASSERT_PTR(unit_type_properties[t]); *tables; tables++) + for (const BusProperty *item = *tables; item->convert; item++) { + assert(item->name || item->dump); + + /* Do not print deprecated names */ + if (item->convert == warn_deprecated) + continue; + + if (item->name) + puts(item->name); + else + item->dump(); + } +} + int bus_append_scope_pidref(sd_bus_message *m, const PidRef *pidref, bool allow_pidfd) { assert(m); diff --git a/src/shared/bus-unit-util.h b/src/shared/bus-unit-util.h index c6e02a01b4..c07a704414 100644 --- a/src/shared/bus-unit-util.h +++ b/src/shared/bus-unit-util.h @@ -51,3 +51,5 @@ int unit_freezer_new(const char *name, UnitFreezer **ret); int unit_freezer_freeze(UnitFreezer *f); int unit_freezer_thaw(UnitFreezer *f); + +void bus_dump_transient_settings(UnitType t); diff --git a/src/shared/condition.c b/src/shared/condition.c index 14318667ec..25c38f374b 100644 --- a/src/shared/condition.c +++ b/src/shared/condition.c @@ -1402,6 +1402,10 @@ ConditionType condition_type_from_string(const char *s) { return _condition_type_from_string(s); } +void condition_types_list(void) { + DUMP_STRING_TABLE(_condition_type, ConditionType, _CONDITION_TYPE_MAX); +} + static const char* const _assert_type_table[_CONDITION_TYPE_MAX] = { [CONDITION_ARCHITECTURE] = "AssertArchitecture", [CONDITION_FIRMWARE] = "AssertFirmware", @@ -1453,6 +1457,10 @@ ConditionType assert_type_from_string(const char *s) { return _assert_type_from_string(s); } +void assert_types_list(void) { + DUMP_STRING_TABLE(_assert_type, ConditionType, _CONDITION_TYPE_MAX); +} + static const char* const condition_result_table[_CONDITION_RESULT_MAX] = { [CONDITION_UNTESTED] = "untested", [CONDITION_SUCCEEDED] = "succeeded", diff --git a/src/shared/condition.h b/src/shared/condition.h index a06d2a89d1..c25b9643b9 100644 --- a/src/shared/condition.h +++ b/src/shared/condition.h @@ -88,9 +88,11 @@ void condition_dump_list(Condition *c, FILE *f, const char *prefix, condition_to const char* condition_type_to_string(ConditionType t) _const_; ConditionType condition_type_from_string(const char *s) _pure_; +void condition_types_list(void); const char* assert_type_to_string(ConditionType t) _const_; ConditionType assert_type_from_string(const char *s) _pure_; +void assert_types_list(void); const char* condition_result_to_string(ConditionResult r) _const_; ConditionResult condition_result_from_string(const char *s) _pure_; diff --git a/src/test/meson.build b/src/test/meson.build index 6bb9e3f40d..286fcac5c7 100644 --- a/src/test/meson.build +++ b/src/test/meson.build @@ -56,6 +56,7 @@ simple_tests += files( 'test-blockdev-util.c', 'test-bootspec.c', 'test-build-path.c', + 'test-bus-unit-util.c', 'test-bus-util.c', 'test-calendarspec.c', 'test-cgroup-util.c', diff --git a/src/test/test-bus-unit-util.c b/src/test/test-bus-unit-util.c new file mode 100644 index 0000000000..a1f0104bc0 --- /dev/null +++ b/src/test/test-bus-unit-util.c @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "bus-unit-util.h" +#include "unit-def.h" +#include "tests.h" + +TEST(bus_dump_transient_settings) { + /* -1 is for generic unit, natural numbers are for specific unit types */ + for (UnitType t = 0; t < _UNIT_TYPE_MAX; t++) { + log_info("==================== %s ====================", t < 0 ? "unit" : unit_type_to_string(t)); + bus_dump_transient_settings(t); + } +} + +DEFINE_TEST_MAIN(LOG_DEBUG); diff --git a/src/test/test-conf-parser.c b/src/test/test-conf-parser.c index 77e321d94c..e2f9f28792 100644 --- a/src/test/test-conf-parser.c +++ b/src/test/test-conf-parser.c @@ -171,14 +171,14 @@ TEST(config_parse_unsigned) { } TEST(config_parse_strv) { - test_config_parse_strv_one("", false, STRV_MAKE_EMPTY); + test_config_parse_strv_one("", false, STRV_EMPTY); test_config_parse_strv_one("foo", false, STRV_MAKE("foo")); test_config_parse_strv_one("foo bar foo", false, STRV_MAKE("foo", "bar", "foo")); test_config_parse_strv_one("\"foo bar\" foo", false, STRV_MAKE("foo bar", "foo")); test_config_parse_strv_one("\xc3\x80", false, STRV_MAKE("\xc3\x80")); test_config_parse_strv_one("\xc3\x7f", false, STRV_MAKE("\xc3\x7f")); - test_config_parse_strv_one("", true, STRV_MAKE_EMPTY); + test_config_parse_strv_one("", true, STRV_EMPTY); test_config_parse_strv_one("foo", true, STRV_MAKE("foo")); test_config_parse_strv_one("foo bar foo", true, STRV_MAKE("foo", "bar")); test_config_parse_strv_one("\"foo bar\" foo", true, STRV_MAKE("foo bar", "foo")); diff --git a/src/test/test-nulstr-util.c b/src/test/test-nulstr-util.c index 5110435778..bb0f1b32d1 100644 --- a/src/test/test-nulstr-util.c +++ b/src/test/test-nulstr-util.c @@ -42,22 +42,22 @@ TEST(strv_parse_nulstr_full) { STRV_MAKE("hoge", "hoge2", "hoge3", "", "hoge5", "", "xxx")); strv_parse_nulstr_full_one(((const char[0]) {}), 0, - STRV_MAKE_EMPTY, STRV_MAKE_EMPTY); + STRV_EMPTY, STRV_EMPTY); strv_parse_nulstr_full_one(((const char[1]) { 0 }), 1, - STRV_MAKE(""), STRV_MAKE_EMPTY); + STRV_MAKE(""), STRV_EMPTY); strv_parse_nulstr_full_one(((const char[1]) { 'x' }), 1, STRV_MAKE("x"), STRV_MAKE("x")); strv_parse_nulstr_full_one(((const char[2]) { 0, 0 }), 2, - STRV_MAKE("", ""), STRV_MAKE_EMPTY); + STRV_MAKE("", ""), STRV_EMPTY); strv_parse_nulstr_full_one(((const char[2]) { 'x', 0 }), 2, STRV_MAKE("x"), STRV_MAKE("x")); strv_parse_nulstr_full_one(((const char[3]) { 0, 0, 0 }), 3, - STRV_MAKE("", "", ""), STRV_MAKE_EMPTY); + STRV_MAKE("", "", ""), STRV_EMPTY); strv_parse_nulstr_full_one(((const char[3]) { 'x', 0, 0 }), 3, STRV_MAKE("x", ""), STRV_MAKE("x")); diff --git a/src/test/test-proc-cmdline.c b/src/test/test-proc-cmdline.c index 478974b0a9..68c4e77f75 100644 --- a/src/test/test-proc-cmdline.c +++ b/src/test/test-proc-cmdline.c @@ -244,7 +244,7 @@ TEST(proc_cmdline_key_startswith) { TEST(proc_cmdline_filter_pid1_args) { test_proc_cmdline_filter_pid1_args_one("systemd\0", - STRV_MAKE_EMPTY); + STRV_EMPTY); /* short option */ test_proc_cmdline_filter_pid1_args_one("systemd\0" diff --git a/src/test/test-serialize.c b/src/test/test-serialize.c index 86b1f5f2e0..bd66722456 100644 --- a/src/test/test-serialize.c +++ b/src/test/test-serialize.c @@ -110,7 +110,7 @@ TEST(serialize_strv) { log_info("/* %s (%s) */", __func__, fn); assert_se(serialize_strv(f, "strv1", NULL) == 0); - assert_se(serialize_strv(f, "strv2", STRV_MAKE_EMPTY) == 0); + assert_se(serialize_strv(f, "strv2", STRV_EMPTY) == 0); assert_se(serialize_strv(f, "strv3", strv) == 1); assert_se(serialize_strv(f, "strv4", STRV_MAKE(long_string)) == -EINVAL); diff --git a/src/test/test-socket-bind.c b/src/test/test-socket-bind.c index f6f48f6723..fec8269f2b 100644 --- a/src/test/test-socket-bind.c +++ b/src/test/test-socket-bind.c @@ -139,7 +139,7 @@ int main(int argc, char *argv[]) { assert_se(test_socket_bind(m, "socket_bind_test.service", netcat_path, "2000", STRV_MAKE("ipv6:2001-2002"), STRV_MAKE("any")) >= 0); assert_se(test_socket_bind(m, "socket_bind_test.service", netcat_path, "6666", STRV_MAKE("ipv4:6666", "6667"), STRV_MAKE("any")) >= 0); assert_se(test_socket_bind(m, "socket_bind_test.service", netcat_path, "6666", STRV_MAKE("6667", "6668", ""), STRV_MAKE("any")) >= 0); - assert_se(test_socket_bind(m, "socket_bind_test.service", netcat_path, "7777", STRV_MAKE_EMPTY, STRV_MAKE_EMPTY) >= 0); + assert_se(test_socket_bind(m, "socket_bind_test.service", netcat_path, "7777", STRV_EMPTY, STRV_EMPTY) >= 0); assert_se(test_socket_bind(m, "socket_bind_test.service", netcat_path, "8888", STRV_MAKE("any"), STRV_MAKE("any")) >= 0); assert_se(test_socket_bind(m, "socket_bind_test.service", netcat_path, "8888", STRV_MAKE("ipv6:tcp:8888-8889"), STRV_MAKE("any")) >= 0); assert_se(test_socket_bind(m, "socket_bind_test.service", netcat_path, "10000", STRV_MAKE("ipv6:udp:9999-10000"), STRV_MAKE("any")) >= 0); diff --git a/src/test/test-string-util.c b/src/test/test-string-util.c index 2439dd12ec..90ac579707 100644 --- a/src/test/test-string-util.c +++ b/src/test/test-string-util.c @@ -496,7 +496,7 @@ TEST(foreach_word_quoted) { true); check("test\\", - STRV_MAKE_EMPTY, + STRV_EMPTY, true); } diff --git a/src/test/test-strv.c b/src/test/test-strv.c index d2524fb52b..88505993cd 100644 --- a/src/test/test-strv.c +++ b/src/test/test-strv.c @@ -224,9 +224,9 @@ static void test_strv_unquote_one(const char *quoted, char **list) { TEST(strv_unquote) { test_strv_unquote_one(" foo=bar \"waldo\" zzz ", STRV_MAKE("foo=bar", "waldo", "zzz")); - test_strv_unquote_one("", STRV_MAKE_EMPTY); - test_strv_unquote_one(" ", STRV_MAKE_EMPTY); - test_strv_unquote_one(" ", STRV_MAKE_EMPTY); + test_strv_unquote_one("", STRV_EMPTY); + test_strv_unquote_one(" ", STRV_EMPTY); + test_strv_unquote_one(" ", STRV_EMPTY); test_strv_unquote_one(" x", STRV_MAKE("x")); test_strv_unquote_one("x ", STRV_MAKE("x")); test_strv_unquote_one(" x ", STRV_MAKE("x")); @@ -774,7 +774,7 @@ TEST(strv_foreach_backwards) { STRV_FOREACH_BACKWARDS(check, (char**) NULL) assert_not_reached(); - STRV_FOREACH_BACKWARDS(check, STRV_MAKE_EMPTY) + STRV_FOREACH_BACKWARDS(check, STRV_EMPTY) assert_not_reached(); unsigned count = 0; @@ -1049,7 +1049,7 @@ TEST(strv_fnmatch) { _cleanup_strv_free_ char **v = NULL; size_t pos; - assert_se(!strv_fnmatch(STRV_MAKE_EMPTY, "a")); + assert_se(!strv_fnmatch(STRV_EMPTY, "a")); v = strv_new("xxx", "*\\*", "yyy"); assert_se(!strv_fnmatch_full(v, "\\", 0, NULL)); diff --git a/src/test/test-verbs.c b/src/test/test-verbs.c index 03b4836e90..a28fc9b55b 100644 --- a/src/test/test-verbs.c +++ b/src/test/test-verbs.c @@ -43,7 +43,7 @@ TEST(verbs) { test_dispatch_one(STRV_MAKE("copy-to", "foo", "bar", "baz", "quux", "qaax"), verbs, -EINVAL); /* no verb, but a default is set */ - test_dispatch_one(STRV_MAKE_EMPTY, verbs, 0); + test_dispatch_one(STRV_EMPTY, verbs, 0); } TEST(verbs_no_default) { diff --git a/test/units/TEST-07-PID1.delegate-namespaces.sh b/test/units/TEST-07-PID1.delegate-namespaces.sh index 6d8d51caff..3fd1287fb2 100755 --- a/test/units/TEST-07-PID1.delegate-namespaces.sh +++ b/test/units/TEST-07-PID1.delegate-namespaces.sh @@ -45,8 +45,8 @@ testcase_pid() { } testcase_uts() { - (! systemd-run -p PrivateUsersEx=self -p ProtectHostnameEx=private --wait --pipe -- hostname abc) - systemd-run -p PrivateUsersEx=self -p ProtectHostnameEx=private -p DelegateNamespaces=uts --wait --pipe -- hostname abc + (! systemd-run -p PrivateUsersEx=self -p ProtectHostname=private --wait --pipe -- hostname abc) + systemd-run -p PrivateUsersEx=self -p ProtectHostname=private -p DelegateNamespaces=uts --wait --pipe -- hostname abc } testcase_implied_private_users_self() { diff --git a/test/units/TEST-07-PID1.protect-hostname.sh b/test/units/TEST-07-PID1.protect-hostname.sh index 4633dab57b..4a18fe5e21 100755 --- a/test/units/TEST-07-PID1.protect-hostname.sh +++ b/test/units/TEST-07-PID1.protect-hostname.sh @@ -22,7 +22,7 @@ testcase_yes() { (! systemd-run --wait -p ProtectHostname=yes hostname foo) # ProtectHostname=yes can optionally take a hostname. - systemd-run --wait -p ProtectHostnameEx=yes:hoge \ + systemd-run --wait -p ProtectHostname=yes:hoge \ -P bash -xec ' test "$(hostname)" = "hoge" (! hostname foo) @@ -50,10 +50,18 @@ EOF systemd-run --wait -p ProtectHostname=yes -p PrivateMounts=yes \ findmnt --mountpoint /proc/sys/kernel/hostname + + # Check that ProtectHostnameEx=… also works. + systemd-run --wait -p ProtectHostnameEx=yes:hoge \ + -P bash -xec ' + test "$(hostname)" = "hoge" + (! hostname foo) + test "$(hostname)" = "hoge" + ' } testcase_private() { - systemd-run --wait -p ProtectHostnameEx=private \ + systemd-run --wait -p ProtectHostname=private \ -P bash -xec ' hostname foo test "$(hostname)" = "foo" @@ -64,7 +72,7 @@ testcase_private() { test "$(hostnamectl hostname)" = "$HOSTNAME_FROM_SYSTEMD" # ProtectHostname=private can optionally take a hostname. - systemd-run --wait -p ProtectHostnameEx=private:hoge \ + systemd-run --wait -p ProtectHostname=private:hoge \ -P bash -xec ' test "$(hostname)" = "hoge" hostname foo @@ -91,7 +99,7 @@ EOF test "$(hostnamectl hostname)" = "$HOSTNAME_FROM_SYSTEMD" # Verify /proc/sys/kernel/hostname is not bind mounted from host read-only. - (! systemd-run --wait -p ProtectHostnameEx=private -p PrivateMounts=yes \ + (! systemd-run --wait -p ProtectHostname=private -p PrivateMounts=yes \ findmnt --mountpoint /proc/sys/kernel/hostname) } diff --git a/test/units/TEST-07-PID1.transient-unit-container.sh b/test/units/TEST-07-PID1.transient-unit-container.sh index f18e9ff112..60316bf2fa 100755 --- a/test/units/TEST-07-PID1.transient-unit-container.sh +++ b/test/units/TEST-07-PID1.transient-unit-container.sh @@ -157,7 +157,7 @@ testcase_transient_unit_container_file_write() { -p RootDirectory="$CONTAINER_ROOT_FS" \ -p PrivatePIDs=yes \ -p PrivateUsersEx=full \ - -p ProtectHostnameEx=private \ + -p ProtectHostname=private \ -p ProtectControlGroupsEx=private \ -p PrivateMounts=yes \ -p PrivateNetwork=yes \ diff --git a/test/units/TEST-54-CREDS.sh b/test/units/TEST-54-CREDS.sh index dae8d6a242..7016aee85a 100755 --- a/test/units/TEST-54-CREDS.sh +++ b/test/units/TEST-54-CREDS.sh @@ -334,7 +334,7 @@ systemd-run -p "ImportCredential=test.creds.*" \ test ! -e '${CREDENTIALS_DIRECTORY}/test.creds.hoge:invalid' # Check if credentials with invalid names are not imported (with renaming). -systemd-run -p "ImportCredentialEx=test.creds.*:renamed.creds." \ +systemd-run -p "ImportCredential=test.creds.*:renamed.creds." \ --unit=test-54-ImportCredential.service \ -p DynamicUser=1 \ --wait \ @@ -352,8 +352,8 @@ systemd-run -p "ImportCredential=test.creds.*" \ '${CREDENTIALS_DIRECTORY}/test.creds.third' >/tmp/ts54-concat cmp /tmp/ts54-concat <(echo -n abc) -# Check that ImportCredentialEx= works without renaming. -systemd-run -p "ImportCredentialEx=test.creds.*" \ +# Check that ImportCredential= works without renaming. +systemd-run -p "ImportCredential=test.creds.*" \ --unit=test-54-ImportCredential.service \ -p DynamicUser=1 \ --wait \ @@ -364,7 +364,7 @@ systemd-run -p "ImportCredentialEx=test.creds.*" \ cmp /tmp/ts54-concat <(echo -n abc) # Check that renaming with globs works as expected. -systemd-run -p "ImportCredentialEx=test.creds.*:renamed.creds." \ +systemd-run -p "ImportCredential=test.creds.*:renamed.creds." \ --unit=test-54-ImportCredential.service \ -p DynamicUser=1 \ --wait \ @@ -375,7 +375,7 @@ systemd-run -p "ImportCredentialEx=test.creds.*:renamed.creds." \ cmp /tmp/ts54-concat <(echo -n abc) # Check that renaming without globs works as expected. -systemd-run -p "ImportCredentialEx=test.creds.first:renamed.creds.first" \ +systemd-run -p "ImportCredential=test.creds.first:renamed.creds.first" \ --unit=test-54-ImportCredential.service \ -p DynamicUser=1 \ --wait \ @@ -384,8 +384,8 @@ systemd-run -p "ImportCredentialEx=test.creds.first:renamed.creds.first" \ cmp /tmp/ts54-concat <(echo -n a) # Test that multiple renames are processed in the correct order. -systemd-run -p "ImportCredentialEx=test.creds.first:renamed.creds.first" \ - -p "ImportCredentialEx=test.creds.second:renamed.creds.first" \ +systemd-run -p "ImportCredential=test.creds.first:renamed.creds.first" \ + -p "ImportCredential=test.creds.second:renamed.creds.first" \ --unit=test-54-ImportCredential.service \ -p DynamicUser=1 \ --wait \ @@ -394,6 +394,7 @@ systemd-run -p "ImportCredentialEx=test.creds.first:renamed.creds.first" \ cmp /tmp/ts54-concat <(echo -n a) # Test that a credential can be imported multiple times with different names. +# We use the deprecated name ImportCredentialEx= on purpose to check that it works. systemd-run -p "ImportCredentialEx=test.creds.first" \ -p "ImportCredentialEx=test.creds.first:renamed.creds.first" \ -p "ImportCredentialEx=test.creds.first:renamed.creds.second" \ diff --git a/test/units/TEST-65-ANALYZE.sh b/test/units/TEST-65-ANALYZE.sh index ac4b220ed9..468c9ed093 100755 --- a/test/units/TEST-65-ANALYZE.sh +++ b/test/units/TEST-65-ANALYZE.sh @@ -1105,6 +1105,17 @@ else echo "have no tpm2" fi +# Test "transient-settings" verb + +# shellcheck disable=SC2046 +systemd-analyze --no-pager transient-settings $(systemctl --no-legend --no-pager -t help) +systemd-analyze transient-settings service | grep NoNewPrivileges +systemd-analyze transient-settings mount | grep CPUQuotaPeriodSec +# make sure deprecated names are not printed +(! systemd-analyze transient-settings service | grep CPUAccounting ) +(! systemd-analyze transient-settings service | grep ConditionKernelVersion ) +(! systemd-analyze transient-settings service | grep AssertKernelVersion ) + systemd-analyze log-level info touch /testok