diff --git a/src/test/test-bus-unit-util.c b/src/test/test-bus-unit-util.c index a1f0104bc0..a5207fa02d 100644 --- a/src/test/test-bus-unit-util.c +++ b/src/test/test-bus-unit-util.c @@ -1,15 +1,1167 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include "sd-bus.h" + #include "bus-unit-util.h" +#include "extract-word.h" #include "unit-def.h" +#include "strv.h" #include "tests.h" +static sd_bus *arg_bus = NULL; +STATIC_DESTRUCTOR_REGISTER(arg_bus, sd_bus_unrefp); + +static void test_transient_settings_one(UnitType type, const char* const* lines) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + int r; + + if (!arg_bus) + return (void) log_tests_skipped("no bus connection"); + + ASSERT_OK(sd_bus_message_new(arg_bus, &m, SD_BUS_MESSAGE_METHOD_CALL)); + + STRV_FOREACH(s, lines) { + const char *t = *s; + int expect = 1; + + if (t[0] == '-') { + _cleanup_free_ char *code = NULL; + ASSERT_OK(extract_first_word(&t, &code, " ", 0)); + ASSERT_OK(r = errno_from_name(code + 1)); + expect = -r; + } + + r = bus_append_unit_property_assignment(m, type, t); + log_debug("%s → %d/%s", t, r, r < 0 ? ERRNO_NAME_FULL(r) : yes_no(r)); + ASSERT_EQ(r, expect); + } +} + +/* The tests data below is in a format intended to be easy to read and write: + * Examples can be plain or prefixed with a negative numeric error code: + * - unadorned examples must return 1 (success), + * - otherwise, the function must return the given error. + * Note that some functions leave the message in a broken state and subsequent + * attempts to extend the message will return -ENXIO. + */ + +TEST(cgroup_properties) { + const char* const *lines = STRV_MAKE_CONST( + "DevicePolicy=strict", + "DevicePolicy=auto", + "DevicePolicy=closed", + + "Slice=system.slice", + "Slice=user-1000.slice", + "Slice=system-getty-this-is-a-very-long-long-slice.slice", + "Slice=", + + "ManagedOOMSwap=auto", + "ManagedOOMSwap=kill", + "ManagedOOMSwap=", + + "ManagedOOMMemoryPressure=auto", + "ManagedOOMMemoryPressure=kill", + "ManagedOOMMemoryPressure=", + + "ManagedOOMPreference=none", + "ManagedOOMPreference=avoid", + "ManagedOOMPreference=omit", + + "MemoryPressureWatch=auto", + "MemoryPressureWatch=off", + "MemoryPressureWatch=on", + "MemoryPressureWatch=skip", + + "DelegateSubgroup=foo", + "DelegateSubgroup=bar.scope", + "DelegateSubgroup=", + + "MemoryAccounting=true", + "-EINVAL MemoryAccounting=false ", + "-EINVAL MemoryAccounting= yes", + "-EINVAL MemoryAccounting=\t\tno\t\t", + "MemoryAccounting=1", + "MemoryAccounting=0", + "MemoryAccounting=on", + "MemoryAccounting=off", + "MemoryAccounting=True", + "MemoryAccounting=FALSE", + "MemoryAccounting=YES", + "MemoryAccounting=No", + + "MemoryZSwapWriteback=true", + "MemoryZSwapWriteback=false", + "IOAccounting=yes", + "IOAccounting=no", + "TasksAccounting=1", + "TasksAccounting=0", + "IPAccounting=on", + "IPAccounting=off", + "CoredumpReceive=true", + "CoredumpReceive=false", + + "CPUWeight=100", + "StartupCPUWeight=100", + "IOWeight=100", + "StartupIOWeight=100", + + "AllowedCPUs=0-3", + "AllowedCPUs=1,3,5,7", + "StartupAllowedCPUs=0-1", + "StartupAllowedCPUs=2,4,6", + + "AllowedMemoryNodes=0", + "AllowedMemoryNodes=0-2", + "AllowedMemoryNodes=0-2 3 5 7 3 2 1", + "StartupAllowedMemoryNodes=0", + "StartupAllowedMemoryNodes=1-3", + + "DisableControllers=cpu", + "DisableControllers= " + " cpu cpuacct cpuset io blkio memory devices pids bpf-firewall bpf-devices " + " cpu cpuacct cpuset io blkio memory\tdevices\tpids\tbpf-firewall\tbpf-devices ", + + "Delegate=yes", + + "MemoryMin=100M", + "DefaultMemoryLow=50M", + "DefaultMemoryMin=10M", + "MemoryLow=2T", + "MemoryHigh=4G", + "MemoryMax=2G", + "MemoryMax=infinity", + "MemorySwapMax=1G", + "MemorySwapMax=0", + "MemoryZSwapMax=500M", + "MemoryZSwapMax=1G", + "TasksMax=1000", + "TasksMax=infinity", + + "CPUQuota=1%", + "CPUQuota=200%", + "CPUQuotaPeriodSec=100ms", + "CPUQuotaPeriodSec=1s", + + "DeviceAllow=/dev/null rw", + "DeviceAllow=/dev/zero r", + + "IODeviceWeight=/dev/sda 200", + "IODeviceWeight=/dev/nvme0n1 500", + "IODeviceLatencyTargetSec=/dev/sda 100ms", + "IODeviceLatencyTargetSec=/dev/nvme0n1 10ms", + + "IPAddressAllow=10.0.0.0/8", + "IPAddressAllow=192.168.1.0/24 ::1", + "IPAddressDeny=0.0.0.0/0", + "IPAddressDeny=192.168.100.0/24", + + "IPIngressFilterPath=/etc/systemd/bpf/ingress.bpf", + "IPEgressFilterPath=/etc/systemd/bpf/egress.bpf", + + "BPFProgram=/usr/lib/systemd/bpf/custom.bpf", + + "SocketBindAllow=tcp:80", + "SocketBindAllow=udp:53", + "SocketBindDeny=tcp:1-1023", + "SocketBindDeny=udp:9999-9999", + "SocketBindDeny=any", + + "MemoryPressureThresholdSec=1s", + "MemoryPressureThresholdSec=1 min 5s 23ms", + + "NFTSet=cgroup:inet:filter:my_service user:inet:filter:serviceuser", + + "ManagedOOMMemoryPressureDurationSec=30s", + "ManagedOOMMemoryPressureDurationSec=infinity", + "ManagedOOMMemoryPressureLimit=0%", + "ManagedOOMMemoryPressureLimit=95.8%", + "ManagedOOMMemoryPressureLimit=95.88%", + "-EINVAL ManagedOOMMemoryPressureLimit=95.888%", /* too many digits after dot */ + "ManagedOOMMemoryPressureLimit=100%", + + "IOReadBandwidthMax=/dev/sda 1M", + "IOReadBandwidthMax=/dev/sdb 2000K", + "IOReadBandwidthMax=/dev/nvme0n1 100M", + "IOReadBandwidthMax=/dev/nvme0n1p1 1G", + "IOReadBandwidthMax=/dev/mapper/root infinity", + "IOWriteBandwidthMax=/dev/sda 1M", + "IOWriteBandwidthMax=/dev/sdb 2M", + "IOWriteBandwidthMax=/dev/nvme0n1 100M", + "IOWriteBandwidthMax=/dev/mapper/home infinity", + + /* Various strange cases */ + "DevicePolicy=", + "DevicePolicy=value=with=equals", + "DevicePolicy=auto domain", + "DevicePolicy=policy with spaces", + "Slice=complex-name_with.various-chars123", + "ManagedOOMSwap=kill immediate", + "ManagedOOMMemoryPressureLimit=50.5%", + "IOReadBandwidthMax=/dev/disk/by-uuid/12345678-1234-1234-1234-123456789012 100M", + "IOWriteBandwidthMax=/dev/disk/by-label/DATA 200M", + "IOReadBandwidthMax=/dev/disk/by-partlabel/EFI\\x20System\\x20Partition 500M", + "IOWriteBandwidthMax=/dev/disk/by-id/dm-uuid-CRYPT-LUKS2-00f7541884c34bff92f60faec9d8f217-luks-00f75418-84c3-4bff-92f6-0faec9d8f217 500M", + "DelegateSubgroup=very.long.subgroup.name.with.dots", + "MemoryPressureWatch=custom-value", + + /* Properties with special characters and edge cases */ + "Slice=user-$(id -u).slice", + "DelegateSubgroup=test@service.scope", + "DevicePolicy=auto,strict", + "ManagedOOMSwap=auto;kill", + + /* Deprecated */ + "MemoryLimit=1G", + "MemoryLimit=infinity", + "MemoryLimit=", + "CPUShares=1024", + "StartupCPUShares=1024", + "BlockIOAccounting=true", + "BlockIOWeight=100", + "StartupBlockIOWeight=1000", + "BlockIODeviceWeight=/dev/sda 500", + "BlockIOReadBandwidth=/dev/sda 1M", + "BlockIOWriteBandwidth=/dev/sda 1M", + "CPUAccounting=true" + ); + + test_transient_settings_one(UNIT_SERVICE, lines); + /* Also UNIT_SOCKET, UNIT_SLICE, UNIT_SCOPE, UNIT_MOUNT. */ +} + +TEST(automount_properties) { + const char* const *lines = STRV_MAKE_CONST( + "Where=/mnt/data", + "Where=/home/user/./../storage", + "Where=non/absolute/path", // TODO: should this be allowed? + + "ExtraOptions=uid=1000,gid=1000", + "ExtraOptions= rw,noatime,user_xattr ", + + "DirectoryMode=0755", + "DirectoryMode=700", + + "TimeoutIdleSec=60s", + "TimeoutIdleSec=1week 1 day 2h 3 minutes 300s 50 ms" + ); + + test_transient_settings_one(UNIT_AUTOMOUNT, lines); +} + +TEST(execute_properties) { + const char* const *lines = STRV_MAKE_CONST( + /* String properties */ + "User=myuser", + "Group=mygroup", + "UtmpIdentifier=myservice", + "UtmpMode=init", + "UtmpMode=login", + "PAMName=login", + "PAMName=system-auth", + "TTYPath=/dev/tty1", + "TTYPath=/dev/pts/0", + "WorkingDirectory=/var/lib/myapp", + "RootDirectory=/srv/container", + "SyslogIdentifier=myservice", + "ProtectSystem=strict", + "ProtectSystem=true", + "ProtectSystem=false", + "ProtectHome=true", + "ProtectHome=read-only", + "ProtectHome=tmpfs", + + "PrivateTmpEx=true", + "PrivateTmpEx=false", + "PrivateUsersEx=true", + "PrivateUsersEx=false", + "ProtectControlGroupsEx=true", + "ProtectControlGroupsEx=false", + + "SELinuxContext=system_u:system_r:httpd_t:s0", + "RootImage=/var/lib/machines/container.raw", + "RootVerity=/var/lib/machines/container.verity", + "RuntimeDirectoryPreserve=true", + "RuntimeDirectoryPreserve=restart", + "Personality=x86-64", + "Personality=x86", + "KeyringMode=inherit", + "KeyringMode=private", + "KeyringMode=shared", + "ProtectProc=invisible", + "ProtectProc=noaccess", + "ProtectProc=ptraceable", + "ProcSubset=pid", + "ProcSubset=all", + "NetworkNamespacePath=/proc/123/ns/net", + "IPCNamespacePath=/proc/123/ns/ipc", + "LogNamespace=myapp", + "RootImagePolicy=closed", + "RootImagePolicy=strict", + "MountImagePolicy=closed", + "MountImagePolicy=strict", + "ExtensionImagePolicy=closed", + "ExtensionImagePolicy=strict", + + /* PrivatePIDs is boolean, but a string. */ + "PrivatePIDs=true", + "PrivatePIDs=false", + "PrivatePIDs=foooooooooo", + + /* Boolean properties */ + "IgnoreSIGPIPE=true", + "TTYVHangup=true", + "TTYReset=true", + "TTYVTDisallocate=true", + "PrivateTmp=true", + "PrivateDevices=true", + "PrivateNetwork=true", + "PrivateUsers=true", + "PrivateMounts=true", + "PrivateIPC=true", + "NoNewPrivileges=true", + "SyslogLevelPrefix=true", + "MemoryDenyWriteExecute=true", + "RestrictRealtime=true", + "DynamicUser=true", + "RemoveIPC=true", + "ProtectKernelTunables=true", + "ProtectKernelModules=true", + "ProtectKernelLogs=true", + "ProtectClock=true", + "ProtectControlGroups=true", + "MountAPIVFS=true", + "BindLogSockets=true", + "CPUSchedulingResetOnFork=true", + "LockPersonality=true", + "MemoryKSM=true", + "RestrictSUIDSGID=true", + "RootEphemeral=true", + "SetLoginEnvironment=true", + + /* Path and directory strvs */ + "ReadWriteDirectories=/var/lib/myapp", + "ReadWriteDirectories=/var/lib/myapp /tmp/workdir", + "ReadOnlyDirectories=/usr/share/data", + "ReadOnlyDirectories=/usr/share/data /etc/config", + "InaccessibleDirectories=/home /root", + "ReadWritePaths=/var/lib/myapp /tmp/workdir", + "ReadOnlyPaths=", + "ReadOnlyPaths=/ /./ /../../ /usr/share/data /etc/config", + "InaccessiblePaths=/home", + "InaccessiblePaths=/home /root /var/cache", + "ExecPaths=/usr/bin /usr/local/bin", + "NoExecPaths=/tmp /var/tmp", + "ExecSearchPath=/usr/bin:/usr/local/bin", + "ExtensionDirectories=/var/lib/extensions /opt/extensions", + "ConfigurationDirectory=myapp subdir", + "SupplementaryGroups=wheel audio video", + "SystemCallArchitectures=native x86-64", + + /* Log levels and facilities */ + "SyslogLevel=info", + "SyslogLevel=debug", + "SyslogLevel=warning", + "SyslogLevel=err", + "-EINVAL SyslogLevel=error", + "LogLevelMax=info", + "LogLevelMax=debug", + "LogLevelMax=warning", + "SyslogFacility=daemon", + "SyslogFacility=local0", + "SyslogFacility=mail", + + /* Various */ + "SecureBits=keep-caps", + "SecureBits=keep-caps-locked", + "SecureBits=no-setuid-fixup", + + "CPUSchedulingPolicy=other", + "CPUSchedulingPolicy=fifo", + "CPUSchedulingPolicy=rr", + "CPUSchedulingPriority=50", + "CPUSchedulingPriority=1", + "CPUSchedulingPriority=99", + + "OOMScoreAdjust=100", + "OOMScoreAdjust=-100", + "OOMScoreAdjust=0", + "CoredumpFilter=0x33", + "CoredumpFilter=0x7f", + "Nice=10", + "Nice=-10", + "SystemCallErrorNumber=EPERM", + "SystemCallErrorNumber=EACCES", + "SystemCallErrorNumber=kill", + + "IOSchedulingClass=1", + "IOSchedulingPriority=4", + + "RuntimeDirectoryMode=0755", + "RuntimeDirectoryMode=750", + "StateDirectoryMode=0000", + "CacheDirectoryMode=0755", + "LogsDirectoryMode=0755", + "ConfigurationDirectoryMode=0755", + "UMask=0002", + + "TimerSlackNSec=50ms", + "LogRateLimitIntervalSec=30s", + "LogRateLimitBurst=1000", + + "TTYRows=24", + "TTYColumns=80", + + "MountFlags=shared", + "MountFlags=slave", + "MountFlags=private", + + "Environment=PATH=/usr/bin:/bin", + "Environment=HOME=/home/user USER=myuser", + "UnsetEnvironment=TEMP", + "UnsetEnvironment=TEMP TMP", + "PassEnvironment=PATH", + "PassEnvironment=PATH HOME USER", + "EnvironmentFile=/etc/default/myservice", + "EnvironmentFile=-/etc/default/myservice", + + "SetCredential=username:nonsecret", + "SetCredential=token1:abcdef123456 token2: token3:asdf:asdf", + "SetCredential=", + /* "-EPIPE SetCredentialEncrypted=password:encrypted_data token:encrypted_too", */ + + "LoadCredential=", + "LoadCredential=asdf", + "LoadCredential=cert:/etc/ssl/cert.pem", + "LoadCredential=key:/etc/ssl/key.pem", + "LoadCredentialEncrypted=password:encrypted_file", + "ImportCredential=*", + "ImportCredential=prefix.*", + "ImportCredentialEx=*", + "ImportCredentialEx=prefix.*", + + "LogExtraFields=FIELD1=value1", + "LogExtraFields=FIELD1=value1 FIELD2=value2", + "LogFilterPatterns=~debug", + "LogFilterPatterns=~debug ~trace", + + "StandardInput=null", + "StandardInput=tty", + "StandardInput=socket", + "StandardOutput=journal", + "StandardOutput=syslog", + "StandardOutput=null", + "StandardError=journal", + "StandardError=syslog", + "StandardError=null", + "StandardInputText=Hello World", + "StandardInputText=Multi\nLine\nText", + "StandardInputData=SGVsbG8gV29ybGQ=", + + "AppArmorProfile=myprofile", + "AppArmorProfile=unconfined", + "SmackProcessLabel=mylabel", + "SmackProcessLabel=_", + + "CapabilityBoundingSet=CAP_NET_BIND_SERVICE", + "CapabilityBoundingSet=CAP_NET_BIND_SERVICE CAP_SYS_TIME", + "AmbientCapabilities=CAP_NET_BIND_SERVICE", + "AmbientCapabilities=CAP_NET_BIND_SERVICE CAP_SYS_TIME", + + "CPUAffinity=0-3", + "CPUAffinity=0,2,4,6", + "CPUAffinity=1", + + "NUMAPolicy=default", + "NUMAPolicy=preferred", + "NUMAPolicy=bind", + "NUMAMask=0-3", + "NUMAMask=0,2,4,6", + + "RestrictAddressFamilies=AF_INET", + "RestrictAddressFamilies=AF_INET AF_INET6", + "RestrictAddressFamilies=~AF_NETLINK", + "RestrictFileSystems=ext4", + "RestrictFileSystems=ext4 xfs", + "RestrictFileSystems=~tmpfs", + "SystemCallFilter=@system-service", + "SystemCallFilter=read write open close", + "SystemCallFilter=~@debug", + "SystemCallLog=@system-service", + "SystemCallLog=read write open", + "RestrictNetworkInterfaces=lo", + "RestrictNetworkInterfaces=lo eth0", + "RestrictNetworkInterfaces=~wlan0", + "RestrictNamespaces=cgroup", + "RestrictNamespaces=cgroup ipc net", + "RestrictNamespaces=~user", + "DelegateNamespaces=pid", + "DelegateNamespaces=pid net", + + "BindPaths=/host/path:/container/path", + "BindPaths=/host/path:/container/path:rbind", + "BindReadOnlyPaths=/host/ro:/container/ro", + "BindReadOnlyPaths=/host/ro:/container/ro:rbind", + + "TemporaryFileSystem=/tmp", + "TemporaryFileSystem=/tmp:rw,nodev,nosuid,size=100M /with/colons/in/options:rw,size=10%:::::::: \"/with/spaces path/:option1:option2\"", + /* "-EINVAL TemporaryFileSystem=\"/tmp path", */ + "TemporaryFileSystem=", + + "RootHash=/a/path", + "RootHash=1234567890abcdef1234567890abcdef", + "RootHashSignature=/a/path", + "RootHashSignature=base64:zKFyIq7aZn4EpuCCmpcF9jPgD8JFE1g/xfT0Mas8X4M0WycyigRsQ4IH4yysufus0AORQsuk3oeGhRC7t1tLyKD0Ih0VcYedv5+p8e6itqrIwzecu98+rNyUVDhWBzS0PMwxEw==", + + "RootImageOptions=partition=root,rw", + "RootImageOptions=partition=usr,ro", + "MountImages=/path/to/image.raw:/mount/point", + "MountImages=/path/to/image.raw:/mount/point:partition=1", + "ExtensionImages=/path/to/ext.raw", + "ExtensionImages=/path/to/ext.raw:/opt/extension", + + "StateDirectory=myapp", + "StateDirectory=myapp subdir", + "RuntimeDirectory=myapp", + "RuntimeDirectory=myapp subdir", + "CacheDirectory=myapp", + "CacheDirectory=myapp subdir", + "LogsDirectory=myapp", + "LogsDirectory=myapp subdir", + + "ProtectHostname=true", + "ProtectHostname=false", + "ProtectHostname=private", + "ProtectHostnameEx=true", + "ProtectHostnameEx=false", + "ProtectHostnameEx=private", + "ProtectHostname=true:foo", + "ProtectHostname=false:foo", + "ProtectHostname=private:foo", + "ProtectHostnameEx=true:foo", + "ProtectHostnameEx=false:foo", + "ProtectHostnameEx=private:foo", + "ProtectHostname=private:a.b.c.d.example.org", + + "LimitCPU=15", + "LimitCPU=15:35", + "LimitCPU=infinity", + "LimitFSIZE=123", + "LimitDATA=1", + "LimitSTACK=1:2", + "LimitCORE=0", + "LimitRSS=0", + "LimitNPROC=0", + "LimitNOFILE=1234", + "LimitMEMLOCK=infinity", + "LimitAS=44", + "LimitLOCKS=22", + "LimitSIGPENDING=11", + "LimitMSGQUEUE=0", + "LimitNICE=0", + "LimitRTPRIO=0", + "LimitRTTIME=0" + ); + + test_transient_settings_one(UNIT_SOCKET, lines); + /* Also UNIT_SERVICE, UNIT_MOUNT. */ +} + +TEST(kill_properties) { + const char* const *lines = STRV_MAKE_CONST( + "KillMode=control-group", + "KillMode=process", + "KillMode=mixed", + "KillMode=none", + + "SendSIGHUP=true", + "SendSIGKILL=true", + + "KillSignal=1", + "KillSignal=64", /* _NSIG == 64 */ + "-EINVAL KillSignal=0", + "-EINVAL KillSignal=65", + "RestartKillSignal=TERM", + "RestartKillSignal=SIGTERM", + "FinalKillSignal=WINCH", + "FinalKillSignal=SIGWINCH", + "FinalKillSignal=2", + "WatchdogSignal=RTMIN", + "WatchdogSignal=RTMIN+0", + "ReloadSignal=RTMAX", + "ReloadSignal=RTMAX-0", + "ReloadSignal=RTMAX-5", + "-EINVAL ReloadSignal=RTMAX-100" + ); + + test_transient_settings_one(UNIT_SCOPE, lines); + /* Also UNIT_SERVICE, UNIT_SOCKET, UNIT_MOUNT. */ +} + +TEST(mount_properties) { + const char* const *lines = STRV_MAKE_CONST( + "What=/dev/sda1", + "What=UUID=12345678-1234-1234-1234-123456789012", + + "Where=/mnt/disk", + "Where=non/absolute/var/lib/data", // TODO: should this be allowed? + + "Options=defaults", + "Options= rw,noatime,user_xattr,acl more ", + "Options=", + + "Type=ext4", + "TimeoutSec= 90 s ", + "DirectoryMode=0755", + "SloppyOptions=true", + "LazyUnmount=true", + "ForceUnmount=true", + "ReadwriteOnly=true" + ); + + test_transient_settings_one(UNIT_MOUNT, lines); +} + +TEST(path_properties) { + const char* const *lines = STRV_MAKE_CONST( + "MakeDirectory=true", + "DirectoryMode=0", + "DirectoryMode=1", + + "PathExists=/var/lib/myapp/ready", + "PathExists=/tmp/../././././././././.../.../.../.../.../.../lockfile", + + "PathExistsGlob=/var/log/*.log", + "PathExistsGlob=/home/*/Desktop???", + + "PathChanged=/etc/myapp.conf", + "PathModified=/etc/passwd", + "DirectoryNotEmpty=/var/spool/mail", + "TriggerLimitBurst=10", + "PollLimitBurst=100", + "TriggerLimitIntervalSec=2s", + "PollLimitIntervalSec=5s" + ); + + test_transient_settings_one(UNIT_PATH, lines); +} + +TEST(scope_properties) { + const char* const *lines = STRV_MAKE_CONST( + "RuntimeMaxSec=3600s", + "RuntimeRandomizedExtraSec=60s", + "TimeoutStopSec=90s", + "OOMPolicy=stop", + "OOMPolicy=continue", + "User=_some_user", + "User=1000", + "Group=mygroup", + "Group=users" + ); + + test_transient_settings_one(UNIT_SCOPE, lines); +} + +TEST(service_properties) { + const char* const *lines = STRV_MAKE_CONST( + /* String properties — no validity checking */ + "PIDFile=/var/run/myservice.pid", + "PIDFile=/run/myservice.pid", + "PIDFile=/tmp/daemon.pid", + "Type=simple", + "Type=forking", + "Type=oneshot", + "Type=dbus", + "Type=notify", + "Type=idle", + "Type=exec", + "Type= asdf ", + "ExitType=main", + "ExitType=cgroup", + "Restart=no", + "Restart=on-success", + "Restart=on-failure", + "Restart=on-abnormal", + "Restart=on-watchdog", + "Restart=on-abort", + "Restart=always", + "RestartMode=normal", + "RestartMode=direct", + "BusName=org.example.MyService", + "BusName=com.company.daemon", + "BusName=net.domain.service", + "NotifyAccess=none", + "NotifyAccess=main", + "NotifyAccess=exec", + "NotifyAccess=all", + "USBFunctionDescriptors=/dev/usb-ffs/myfunction", + "USBFunctionStrings=/dev/usb-ffs/myfunction", + "OOMPolicy=stop", + "OOMPolicy=continue", + "OOMPolicy=kill", + "TimeoutStartFailureMode=terminate", + "TimeoutStartFailureMode=abort", + "TimeoutStartFailureMode=kill", + "TimeoutStopFailureMode=terminate", + "TimeoutStopFailureMode=abort", + "TimeoutStopFailureMode=kill", + "FileDescriptorStorePreserve=no", + "FileDescriptorStorePreserve=yes", + "FileDescriptorStorePreserve=restart", + + /* Boolean properties */ + "PermissionsStartOnly=true", + "RootDirectoryStartOnly=true", + "RemainAfterExit=true", + "GuessMainPID=true", + "-EINVAL GuessMainPID=WAT", + + /* Timespan properties */ + "RestartSec=5s", + "RestartSec=1 y 2 months 30s", + "RestartMaxDelaySec=5min", + "TimeoutStartSec=infinity", + "TimeoutStopSec=infinity", + "TimeoutAbortSec=0", + "RuntimeMaxSec=24h", + "RuntimeRandomizedExtraSec=30s", + "WatchdogSec=30s", + "TimeoutSec=90s", + + /* Unsigned integer values */ + "FileDescriptorStoreMax=0", + "FileDescriptorStoreMax=9999999", + "RestartSteps=10", + + /* Exec command properties */ + "ExecCondition=test -f /etc/myservice.conf", + "ExecStartPre=/usr/bin/mkdir -p /var/lib/myservice", + "ExecStartPre=-/usr/bin/systemctl is-active network.target", + "ExecStartPre=@/usr/bin/systemctl is-active network.target", + "ExecStartPre=:systemctl is-active network.target", + "ExecStartPre=+systemctl is-active network.target", + "ExecStartPre=!systemctl is-active network.target", + "ExecStartPre=|systemctl is-active network.target", + "ExecStartPre=!!true", + "ExecStart=@:+!|foobar bar bar", + "ExecStart=@:+!!|foobar bar bar", + "ExecStartPost=post post" + "ExecConditionEx=/usr/bin/test -f /etc/myservice.conf", + "ExecStartPreEx=/usr/bin/mkdir -p /var/lib/myservice", + "ExecStartEx=/usr/bin/myservice --config /etc/myservice.conf", + "ExecStartPostEx=/usr/bin/logger 'Service started'", + "ExecReload=/bin/kill -HUP $MAINPID", + "ExecStop=kill -TERM $MAINPID", + "ExecStopPost=+logger 'Service stopped'", + "ExecStopPost=rm -f /var/lib/myservice/started", + "ExecReloadEx=/bin/kill -HUP $MAINPID", + "ExecStopEx=/bin/kill -TERM $MAINPID", + "ExecStopPostEx=/usr/bin/logger 'Service stopped'", + + "RestartPreventExitStatus=1", + "RestartPreventExitStatus=1 2 8 SIGTERM", + "RestartPreventExitStatus=SIGKILL SIGTERM", + "RestartForceExitStatus=1", + "RestartForceExitStatus=1 2 8 SIGTERM", + "RestartForceExitStatus=SIGKILL SIGTERM", + "SuccessExitStatus=0", + "SuccessExitStatus=0 1 2", + "SuccessExitStatus=0 SIGTERM SIGKILL", + + "OpenFile=/a/path", + "OpenFile=/etc/myservice.conf:SOME$NAME", + "OpenFile=/etc/myservice.conf:SOME$NAME:read-only", + "OpenFile=/etc/myservice.conf:SOME$NAME:append", + "OpenFile=/etc/myservice.conf:SOME$NAME:truncate", + "OpenFile=/etc/myservice.conf:SOME$NAME:graceful", + "OpenFile=/etc/myservice.conf::read-only,graceful", + "OpenFile=/etc/myservice.conf::truncate,graceful", + "-EINVAL OpenFile=/etc/myservice.conf::append,truncate,read-only,graceful" + ); + + test_transient_settings_one(UNIT_SERVICE, lines); +} + +TEST(socket_properties) { + const char* const *lines = STRV_MAKE_CONST( + /* Boolean properties */ + "Accept=true", + "FlushPending=true", + "Writable=true", + "KeepAlive=true", + "NoDelay=true", + "FreeBind=true", + "Transparent=true", + "Broadcast=true", + "PassCredentials=true", + "PassFileDescriptorsToExec=true", + "PassSecurity=true", + "PassPacketInfo=true", + "ReusePort=true", + "RemoveOnStop=true", + "SELinuxContextFromNet=true", + + /* Integer properties */ + "Priority=6", + "Priority=0000000", + "Priority=-1", + "IPTTL=64", + "IPTTL=255", + "IPTTL=1", + "Mark=1", + "Mark=0", + "Mark=255", + + /* IP TOS properties */ + "IPTOS=0", + "IPTOS=low-delay", + "IPTOS=throughput", + "IPTOS=reliability", + "IPTOS=low-cost", + + /* Unsigned integer properties */ + "Backlog=0", + "Backlog=128", + "MaxConnections=64", + "MaxConnectionsPerSource=10", + "KeepAliveProbes=9", + "TriggerLimitBurst=10", + "PollLimitBurst=10", + + /* Mode properties */ + "SocketMode=0666", + "SocketMode=644", + "DirectoryMode=0755", + "DirectoryMode=0", + + /* 64-bit integer properties */ + "MessageQueueMaxMessages=10", + "MessageQueueMessageSize=8192", + + /* Timespan properties */ + "TimeoutSec=90s", + "TimeoutSec=5min", + "TimeoutSec=infinity", + "KeepAliveTimeSec=7200s", + "KeepAliveTimeSec=2h", + "KeepAliveTimeSec=30min", + "KeepAliveIntervalSec=75s", + "KeepAliveIntervalSec=1min", + "KeepAliveIntervalSec=30s", + "DeferAcceptSec=1w", + "TriggerLimitIntervalSec=2s", + "PollLimitIntervalSec=2s", + "DeferTriggerMaxSec=infinity", + + /* Size properties */ + "ReceiveBuffer=262144", + "ReceiveBuffer=1M", + "ReceiveBuffer=512K", + "SendBuffer=1.M", // TODO: should this accept multiple components? + "PipeSize=512K", + + /* Exec command properties */ + "ExecStartPre=true", + "ExecStartPost=false", + "ExecReload=kill", + "ExecStopPost=signal", + + /* More string properties */ + "SmackLabel=mylabel", + "SmackLabel=_", + "SmackLabelIPIn=mylabel", + "SmackLabelIPIn=_", + "SmackLabelIPOut=mylabel", + "SmackLabelIPOut=_", + "TCPCongestion=cubic", + "TCPCongestion=reno", + "TCPCongestion=bbr", + "BindToDevice=eth0", + "BindToDevice=wlan0", + "BindToDevice=lo", + "BindIPv6Only=default", + "BindIPv6Only=both", + "BindIPv6Only=ipv6-only", + "FileDescriptorName=myfd", + "FileDescriptorName=socket", + "FileDescriptorName=listener", + "SocketUser=myuser", + "SocketUser=www-data", + "SocketGroup=mygroup", + "SocketGroup=www-data", + "Timestamping=off", + "Timestamping=us", + "Timestamping=ns", + "DeferTrigger=on", + "DeferTrigger=off", + "DeferTrigger=all", + + /* Path strv */ + "Symlinks=/var/lib/socket/link", + "Symlinks=/var/lib/socket/link /tmp/socket-link non/abs/path", + + /* Socket protocol properties */ + "SocketProtocol=udp", + "SocketProtocol=tcp", + "SocketProtocol=sctp", + + /* Listen* properties */ + "ListenStream=8080", + "ListenStream=127.0.0.1:8080", + "ListenStream=[::1]:8080", + "ListenStream=/run/myservice.sock", + "ListenDatagram=8080", + "ListenDatagram=127.0.0.1:8080", + "ListenDatagram=[::1]:8080", + "ListenDatagram=/run/myservice.sock", + "ListenSequentialPacket=/run/myservice.sock", + "ListenSequentialPacket=/tmp/socket", + "ListenNetlink=kobject-uevent", + "ListenNetlink=audit", + "ListenNetlink=route", + "ListenSpecial=/dev/log", + "ListenSpecial=/dev/kmsg", + "ListenSpecial=/proc/kmsg", + "ListenMessageQueue=/myqueue", + "ListenMessageQueue=/system/queue", + "ListenFIFO=/var/lib/myservice/fifo", + "ListenFIFO=/tmp/myfifo", + "ListenUSBFunction=/dev/usb-ffs/myfunction" + ); + test_transient_settings_one(UNIT_SOCKET, lines); +} + +TEST(timer_properties) { + const char* const *lines = STRV_MAKE_CONST( + "WakeSystem=true", + "RemainAfterElapse=yes", + "Persistent=true", + "OnTimezoneChange=yes", + "OnClockChange=true", + "FixedRandomDelay=yes", + "DeferReactivation=true", + + "AccuracySec=1s", + "AccuracySec=10min", + "AccuracySec=1h", + "RandomizedDelaySec=30s", + "RandomizedDelaySec=5min", + "RandomizedDelaySec=0", + "RandomizedOffsetSec=15s", + "RandomizedOffsetSec=2min", + "OnActiveSec=10s", + "OnActiveSec=5min", + "OnBootSec=30s", + "OnBootSec=2min", + "OnStartupSec=45s", + "OnStartupSec=1min", + "OnUnitActiveSec=20s", + "OnUnitActiveSec=10min", + "OnUnitInactiveSec=60s", + "OnUnitInactiveSec=30min", + "OnCalendar=daily", + "OnCalendar=Mon,Tue *-*-* 12:00:00", + "OnCalendar=*-*-* 06:00:00", + "OnCalendar=Sat *-*-* 23:00:00" + ); + + test_transient_settings_one(UNIT_TIMER, lines); +} + +TEST(unit_properties) { + const char* const *lines = STRV_MAKE_CONST( + "Description=My Service Unit", + "SourcePath=/etc/systemd/system/myservice.service", + "SourcePath=non/abs/lib/systemd/system/backup.service", + "OnFailureJobMode=replace", + "OnFailureJobMode=fail", + "OnFailureJobMode=isolate", + "JobTimeoutAction=none", + "JobTimeoutAction=reboot", + "JobTimeoutAction=poweroff", + "JobTimeoutRebootArgument=emergency", + "JobTimeoutRebootArgument=--force", + "StartLimitAction=none", + "StartLimitAction=reboot", + "StartLimitAction=reboot-force", + "FailureAction=none", + "FailureAction=restart", + "FailureAction=reboot", + "SuccessAction=none", + "SuccessAction=poweroff", + "SuccessAction=exit", + "RebootArgument=--force", + "RebootArgument=emergency", + "CollectMode=inactive", + "CollectMode=inactive-or-failed", + "StopWhenUnneeded=true", + "StopWhenUnneeded=false", + "RefuseManualStart=yes", + "RefuseManualStart=no", + "RefuseManualStop=true", + "RefuseManualStop=false", + "AllowIsolate=yes", + "AllowIsolate=no", + "IgnoreOnIsolate=true", + "IgnoreOnIsolate=false", + "SurviveFinalKillSignal=yes", + "SurviveFinalKillSignal=no", + "DefaultDependencies=true", + "DefaultDependencies=false", + "JobTimeoutSec=90s", + "JobTimeoutSec=5min", + "JobTimeoutSec=infinity", + "JobRunningTimeoutSec=30min", + "JobRunningTimeoutSec=1h", + "StartLimitIntervalSec=10s", + "StartLimitIntervalSec=60s", + "StartLimitBurst=5", + "StartLimitBurst=9999999", + "StartLimitBurst=0", + "SuccessActionExitStatus=0", + "SuccessActionExitStatus=", + "FailureActionExitStatus=1", + "FailureActionExitStatus=", + "Documentation=man:myservice(8)", + "Documentation=https://example.com/docs", + "Documentation=file:/usr/share/doc/myservice/README", + "RequiresMountsFor=/var/lib/myservice", + "RequiresMountsFor=\t\t\t \t/home\t\t\t\t\t/var/log \t", + "WantsMountsFor=/tmp", + "WantsMountsFor=/opt /usr/local", + "Markers=manual", + "Markers=needs-reload", + "Markers=manual needs-reload", + + + "Requires=a.service b.service", + "Requisite=a.service b.service", + "Wants=a.service b.service", + "BindsTo=a.service b.service", + "PartOf=a.service b.service", + "Upholds=a.service b.service", + "RequiredBy=a.service b.service", + "RequisiteOf=a.service b.service", + "WantedBy=a.service b.service", + "BoundBy=a.service b.service", + "ConsistsOf=a.service b.service", + "UpheldBy=a.service b.service", + "Conflicts=a.service b.service", + "ConflictedBy=a.service b.service", + "Before=a.service b.service", + "After=a.service b.service", + "OnSuccess=a.service b.service", + "OnSuccessOf=a.service b.service", + "OnFailure=a.service b.service", + "OnFailureOf=a.service b.service", + "Triggers=a.service b.service", + "TriggeredBy=a.service b.service", + "PropagatesReloadTo=a.service b.service", + "ReloadPropagatedFrom=a.service b.service", + "PropagatesStopTo=a.service b.service", + "StopPropagatedFrom=a.service b.service", + "JoinsNamespaceOf=a.service b.service", + "References=a.service b.service", + "ReferencedBy=a.service b.service", + "InSlice=a.service b.service", + "SliceOf=a.service b.service", + + // TODO: do better verification here? + "ConditionArchitecture=aarch64", + "ConditionArchitecture=|!aarch64", + "ConditionArchitecture=!aarch64", + "ConditionArchitecture=|aarch64", + "ConditionArchitecture=|!", + "ConditionFirmware=something", + "ConditionVirtualization=|foo", + "ConditionHost=|foo", + "ConditionKernelCommandLine=|foo", + "ConditionVersion=|foo", + "ConditionCredential=|foo", + "ConditionSecurity=|foo", + "ConditionCapability=|foo", + "ConditionACPower=|foo", + "ConditionMemory=|foo", + "ConditionCPUs=|foo", + "ConditionEnvironment=|foo", + "ConditionCPUFeature=|foo", + "ConditionOSRelease=|foo", + "ConditionMemoryPressure=|foo", + "ConditionCPUPressure=|foo", + "ConditionIOPressure=|foo", + "ConditionNeedsUpdate=|foo", + "ConditionFirstBoot=|foo", + "ConditionPathExists=|foo", + "ConditionPathExistsGlob=|foo", + "ConditionPathIsDirectory=|foo", + "ConditionPathIsSymbolicLink=|foo", + "ConditionPathIsMountPoint=|foo", + "ConditionPathIsReadWrite=|foo", + "ConditionPathIsEncrypted=|foo", + "ConditionDirectoryNotEmpty=|foo", + "ConditionFileNotEmpty=|foo", + "ConditionFileIsExecutable=|foo", + "ConditionUser=|foo", + "ConditionGroup=|foo", + "ConditionControlGroupController=|foo", + "ConditionKernelModuleLoaded=|foo", + "AssertArchitecture=|foo", + "AssertFirmware=|foo", + "AssertVirtualization=|foo", + "AssertHost=|foo", + "AssertKernelCommandLine=|foo", + "AssertVersion=|foo", + "AssertCredential=|foo", + "AssertSecurity=|foo", + "AssertCapability=|foo", + "AssertACPower=|foo", + "AssertMemory=|foo", + "AssertCPUs=|foo", + "AssertEnvironment=|foo", + "AssertCPUFeature=|foo", + "AssertOSRelease=|foo", + "AssertMemoryPressure=|foo", + "AssertCPUPressure=|foo", + "AssertIOPressure=|foo", + "AssertNeedsUpdate=|foo", + "AssertFirstBoot=|foo", + "AssertPathExists=|foo", + "AssertPathExistsGlob=|foo", + "AssertPathIsDirectory=|foo", + "AssertPathIsSymbolicLink=|foo", + "AssertPathIsMountPoint=|foo", + "AssertPathIsReadWrite=|foo", + "AssertPathIsEncrypted=|foo", + "AssertDirectoryNotEmpty=|foo", + "AssertFileNotEmpty=|foo", + "AssertFileIsExecutable=|foo", + "AssertUser=|foo", + "AssertGroup=|foo", + "AssertControlGroupController=|foo", + "AssertKernelModuleLoaded=|foo" + ); + + test_transient_settings_one(UNIT_TARGET, lines); + /* All unit types. */ +} + 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); +static int intro(void) { + int r; + + r = sd_bus_default_user(&arg_bus); + if (r < 0) + r = sd_bus_default_system(&arg_bus); + if (r < 0) + log_info_errno(r, "Failed to connect to bus: %m"); + + return EXIT_SUCCESS; +} + +DEFINE_TEST_MAIN_WITH_INTRO(LOG_DEBUG, intro);