mirror of
https://gitlab.steamos.cloud/holo/steamos-manager.git
synced 2025-07-05 06:00:30 -04:00
Merge steamos-workerd in
This commit is contained in:
parent
6f1f1c032c
commit
83e9de9bcb
10 changed files with 1445 additions and 139 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1 +1,2 @@
|
||||||
/target
|
/target
|
||||||
|
/tarpaulin-report.html
|
||||||
|
|
13
.gitlab-ci.yml
Normal file
13
.gitlab-ci.yml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
stages:
|
||||||
|
- test
|
||||||
|
|
||||||
|
image: ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/archlinux:base-devel
|
||||||
|
|
||||||
|
test:
|
||||||
|
stage: test
|
||||||
|
tags:
|
||||||
|
- docker
|
||||||
|
- linux
|
||||||
|
script:
|
||||||
|
- pacman -Sy --noconfirm --needed dbus rust
|
||||||
|
- dbus-run-session cargo test
|
244
Cargo.lock
generated
244
Cargo.lock
generated
|
@ -19,9 +19,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aho-corasick"
|
name = "aho-corasick"
|
||||||
version = "1.0.5"
|
version = "1.1.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0c378d78423fdad8089616f827526ee33c19f2fddbd5de1629152c9593ba4783"
|
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
@ -57,31 +57,6 @@ dependencies = [
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "async-executor"
|
|
||||||
version = "1.8.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "17ae5ebefcc48e7452b4987947920dac9450be1110cadf34d1b8c116bdbaf97c"
|
|
||||||
dependencies = [
|
|
||||||
"async-lock 3.3.0",
|
|
||||||
"async-task",
|
|
||||||
"concurrent-queue",
|
|
||||||
"fastrand",
|
|
||||||
"futures-lite",
|
|
||||||
"slab",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "async-fs"
|
|
||||||
version = "2.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bc19683171f287921f2405677dd2ed2549c3b3bda697a563ebc3a121ace2aba1"
|
|
||||||
dependencies = [
|
|
||||||
"async-lock 3.3.0",
|
|
||||||
"blocking",
|
|
||||||
"futures-lite",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-io"
|
name = "async-io"
|
||||||
version = "2.3.2"
|
version = "2.3.2"
|
||||||
|
@ -141,9 +116,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-recursion"
|
name = "async-recursion"
|
||||||
version = "1.0.5"
|
version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0"
|
checksum = "30c5ef0ede93efbf733c1a727f3b6b5a1060bbedd5600183e66f6e4be4af0ec5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -187,9 +162,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "atomic-waker"
|
name = "atomic-waker"
|
||||||
version = "1.1.1"
|
version = "1.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3"
|
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
|
@ -199,9 +174,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "backtrace"
|
name = "backtrace"
|
||||||
version = "0.3.69"
|
version = "0.3.71"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
|
checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"addr2line",
|
"addr2line",
|
||||||
"cc",
|
"cc",
|
||||||
|
@ -220,9 +195,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "2.4.0"
|
version = "2.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
|
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "block-buffer"
|
name = "block-buffer"
|
||||||
|
@ -251,18 +226,15 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytes"
|
name = "bytes"
|
||||||
version = "1.5.0"
|
version = "1.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
|
checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.83"
|
version = "1.0.90"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
|
checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5"
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
|
@ -278,30 +250,27 @@ checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "concurrent-queue"
|
name = "concurrent-queue"
|
||||||
version = "2.2.0"
|
version = "2.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c"
|
checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cpufeatures"
|
name = "cpufeatures"
|
||||||
version = "0.2.9"
|
version = "0.2.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1"
|
checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-utils"
|
name = "crossbeam-utils"
|
||||||
version = "0.8.16"
|
version = "0.8.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
|
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crypto-common"
|
name = "crypto-common"
|
||||||
|
@ -427,9 +396,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fastrand"
|
name = "fastrand"
|
||||||
version = "2.0.0"
|
version = "2.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764"
|
checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-core"
|
name = "futures-core"
|
||||||
|
@ -445,17 +414,15 @@ checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-lite"
|
name = "futures-lite"
|
||||||
version = "2.0.0"
|
version = "2.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c1155db57329dca6d018b61e76b1488ce9a2e5e44028cac420a5898f4fcef63"
|
checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fastrand",
|
"fastrand",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-io",
|
"futures-io",
|
||||||
"memchr",
|
|
||||||
"parking",
|
"parking",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"waker-fn",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -477,10 +444,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
|
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-io",
|
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
"futures-task",
|
"futures-task",
|
||||||
"memchr",
|
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"pin-utils",
|
"pin-utils",
|
||||||
"slab",
|
"slab",
|
||||||
|
@ -498,9 +463,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.2.10"
|
version = "0.2.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
|
checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
|
@ -509,21 +474,21 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gimli"
|
name = "gimli"
|
||||||
version = "0.28.0"
|
version = "0.28.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
|
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.14.0"
|
version = "0.14.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
|
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.3.2"
|
version = "0.3.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
|
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hex"
|
name = "hex"
|
||||||
|
@ -533,14 +498,36 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "2.0.0"
|
version = "2.2.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
|
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"equivalent",
|
"equivalent",
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "inotify"
|
||||||
|
version = "0.10.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fdd168d97690d0b8c412d6b6c10360277f4d7ee495c5d0d5d5fe0854923255cc"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"futures-core",
|
||||||
|
"inotify-sys",
|
||||||
|
"libc",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "inotify-sys"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
|
@ -561,9 +548,9 @@ checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.6.3"
|
version = "2.7.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c"
|
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memoffset"
|
name = "memoffset"
|
||||||
|
@ -576,18 +563,18 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miniz_oxide"
|
name = "miniz_oxide"
|
||||||
version = "0.7.1"
|
version = "0.7.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
|
checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"adler",
|
"adler",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mio"
|
name = "mio"
|
||||||
version = "0.8.8"
|
version = "0.8.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2"
|
checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"wasi",
|
"wasi",
|
||||||
|
@ -600,7 +587,7 @@ version = "0.28.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4"
|
checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.4.0",
|
"bitflags 2.5.0",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"cfg_aliases",
|
"cfg_aliases",
|
||||||
"libc",
|
"libc",
|
||||||
|
@ -619,18 +606,18 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "object"
|
name = "object"
|
||||||
version = "0.32.1"
|
version = "0.32.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0"
|
checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.18.0"
|
version = "1.19.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
|
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ordered-stream"
|
name = "ordered-stream"
|
||||||
|
@ -644,9 +631,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking"
|
name = "parking"
|
||||||
version = "2.1.0"
|
version = "2.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e"
|
checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project-lite"
|
name = "pin-project-lite"
|
||||||
|
@ -748,20 +735,11 @@ dependencies = [
|
||||||
"getrandom",
|
"getrandom",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "redox_syscall"
|
|
||||||
version = "0.3.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags 1.3.2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.10.3"
|
version = "1.10.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15"
|
checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
|
@ -798,7 +776,7 @@ version = "0.38.32"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89"
|
checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.4.0",
|
"bitflags 2.5.0",
|
||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys",
|
"linux-raw-sys",
|
||||||
|
@ -807,18 +785,18 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.192"
|
version = "1.0.197"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001"
|
checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.192"
|
version = "1.0.197"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1"
|
checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -876,12 +854,12 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "socket2"
|
name = "socket2"
|
||||||
version = "0.5.4"
|
version = "0.5.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e"
|
checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -895,7 +873,12 @@ name = "steamos-manager"
|
||||||
version = "24.3.0"
|
version = "24.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"inotify",
|
||||||
|
"nix",
|
||||||
|
"tempfile",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tokio-stream",
|
||||||
|
"tokio-util",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
"zbus",
|
"zbus",
|
||||||
|
@ -925,15 +908,14 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tempfile"
|
name = "tempfile"
|
||||||
version = "3.8.0"
|
version = "3.10.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef"
|
checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"fastrand",
|
"fastrand",
|
||||||
"redox_syscall",
|
|
||||||
"rustix",
|
"rustix",
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -948,9 +930,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.33.0"
|
version = "1.36.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653"
|
checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace",
|
"backtrace",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
@ -967,15 +949,39 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-macros"
|
name = "tokio-macros"
|
||||||
version = "2.1.0"
|
version = "2.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
|
checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.53",
|
"syn 2.0.53",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-stream"
|
||||||
|
version = "0.1.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"pin-project-lite",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-util"
|
||||||
|
version = "0.7.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
|
"pin-project-lite",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_datetime"
|
name = "toml_datetime"
|
||||||
version = "0.6.5"
|
version = "0.6.5"
|
||||||
|
@ -1037,9 +1043,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "typenum"
|
||||||
version = "1.16.0"
|
version = "1.17.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
|
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uds_windows"
|
name = "uds_windows"
|
||||||
|
@ -1064,12 +1070,6 @@ version = "0.9.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "waker-fn"
|
|
||||||
version = "1.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.11.0+wasi-snapshot-preview1"
|
version = "0.11.0+wasi-snapshot-preview1"
|
||||||
|
@ -1232,9 +1232,9 @@ checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
version = "0.5.15"
|
version = "0.5.40"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc"
|
checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
@ -1256,15 +1256,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c9ff46f2a25abd690ed072054733e0bc3157e3d4c45f41bd183dce09c2ff8ab9"
|
checksum = "c9ff46f2a25abd690ed072054733e0bc3157e3d4c45f41bd183dce09c2ff8ab9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-broadcast",
|
"async-broadcast",
|
||||||
"async-executor",
|
|
||||||
"async-fs",
|
|
||||||
"async-io",
|
|
||||||
"async-lock 3.3.0",
|
|
||||||
"async-process",
|
"async-process",
|
||||||
"async-recursion",
|
"async-recursion",
|
||||||
"async-task",
|
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"blocking",
|
|
||||||
"derivative",
|
"derivative",
|
||||||
"enumflags2",
|
"enumflags2",
|
||||||
"event-listener 5.2.0",
|
"event-listener 5.2.0",
|
||||||
|
|
11
Cargo.toml
11
Cargo.toml
|
@ -3,12 +3,19 @@ name = "steamos-manager"
|
||||||
version = "24.3.0"
|
version = "24.3.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
tempfile = "3"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
strip="symbols"
|
strip="symbols"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
tokio = { version = "1", features = ["fs", "io-std", "macros", "process", "rt-multi-thread", "signal"] }
|
inotify = { version = "0.10", default-features = false, features = ["stream"] }
|
||||||
|
nix = { version = "0.28", default-features = false, features = ["fs"] }
|
||||||
|
tokio = { version = "1", default-features = false, features = ["fs", "io-util", "macros", "rt-multi-thread", "signal", "sync"] }
|
||||||
|
tokio-stream = { version = "0.1", default-features = false }
|
||||||
|
tokio-util = { version = "0.7", default-features = false }
|
||||||
tracing = { version = "0.1", default-features = false }
|
tracing = { version = "0.1", default-features = false }
|
||||||
tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt"] }
|
tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt"] }
|
||||||
zbus = { version = "4", features = ["tokio"] }
|
zbus = { version = "4", default-features = false, features = ["tokio"] }
|
||||||
|
|
|
@ -1,7 +1,15 @@
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=SteamOS Manager Daemon
|
Description=SteamOS Manager Daemon
|
||||||
|
Wants=steamos-log-submitter.service
|
||||||
|
After=steamos-log-submitter.service
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=dbus
|
Type=dbus
|
||||||
BusName=com.steampowered.SteamOSManager1
|
BusName=com.steampowered.SteamOSManager1
|
||||||
|
Environment=RUST_LOG='INFO'
|
||||||
ExecStart=/usr/lib/steamos-manager
|
ExecStart=/usr/lib/steamos-manager
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=1
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
|
601
src/ds_inhibit.rs
Normal file
601
src/ds_inhibit.rs
Normal file
|
@ -0,0 +1,601 @@
|
||||||
|
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||||
|
use anyhow::{Error, Result};
|
||||||
|
use inotify::{Event, EventMask, EventStream, Inotify, WatchDescriptor, WatchMask};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::ffi::OsString;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::thread::sleep;
|
||||||
|
use std::time::Duration;
|
||||||
|
use tokio::fs;
|
||||||
|
use tokio_stream::StreamExt;
|
||||||
|
use tracing::{debug, error, info, warn};
|
||||||
|
|
||||||
|
use crate::{sysbase, Service};
|
||||||
|
|
||||||
|
struct HidNode {
|
||||||
|
id: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Inhibitor {
|
||||||
|
inotify: EventStream<[u8; 512]>,
|
||||||
|
dev_watch: WatchDescriptor,
|
||||||
|
watches: HashMap<WatchDescriptor, HidNode>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HidNode {
|
||||||
|
fn new(id: u32) -> HidNode {
|
||||||
|
HidNode { id }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sys_base(&self) -> PathBuf {
|
||||||
|
PathBuf::from(format!("{}/sys/class/hidraw/hidraw{}/device", sysbase(), self.id).as_str())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hidraw(&self) -> PathBuf {
|
||||||
|
PathBuf::from(format!("{}/dev/hidraw{}", sysbase(), self.id).as_str())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_nodes(&self) -> Result<Vec<PathBuf>> {
|
||||||
|
let mut entries = Vec::new();
|
||||||
|
let mut dir = fs::read_dir(self.sys_base().join("input")).await?;
|
||||||
|
while let Some(entry) = dir.next_entry().await? {
|
||||||
|
let path = entry.path();
|
||||||
|
let mut dir = fs::read_dir(&path).await?;
|
||||||
|
while let Some(entry) = dir.next_entry().await? {
|
||||||
|
if entry
|
||||||
|
.path()
|
||||||
|
.file_name()
|
||||||
|
.map(|e| e.to_string_lossy())
|
||||||
|
.is_some_and(|e| e.starts_with("mouse"))
|
||||||
|
{
|
||||||
|
debug!("Found {}", path.display());
|
||||||
|
entries.push(path.join("inhibited"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(entries)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn can_inhibit(&self) -> bool {
|
||||||
|
debug!("Checking if hidraw{} can be inhibited", self.id);
|
||||||
|
let driver = match fs::read_link(self.sys_base().join("driver")).await {
|
||||||
|
Ok(driver) => driver,
|
||||||
|
Err(e) => {
|
||||||
|
warn!(
|
||||||
|
"Failed to find associated driver for hidraw{}: {}",
|
||||||
|
self.id, e
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if !matches!(
|
||||||
|
driver.file_name().and_then(|d| d.to_str()),
|
||||||
|
Some("sony") | Some("playstation")
|
||||||
|
) {
|
||||||
|
debug!("Not a PlayStation controller");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let nodes = match self.get_nodes().await {
|
||||||
|
Ok(nodes) => nodes,
|
||||||
|
Err(e) => {
|
||||||
|
warn!("Failed to list inputs for hidraw{}: {e}", self.id);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if nodes.is_empty() {
|
||||||
|
debug!("No nodes to inhibit");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn check(&self) -> Result<()> {
|
||||||
|
let hidraw = self.hidraw();
|
||||||
|
let mut dir = fs::read_dir(sysbase() + "/proc").await?;
|
||||||
|
while let Some(entry) = dir.next_entry().await? {
|
||||||
|
let path = entry.path();
|
||||||
|
let proc = match path.file_name().map(|p| p.to_str()) {
|
||||||
|
Some(Some(p)) => p,
|
||||||
|
_ => continue,
|
||||||
|
};
|
||||||
|
let _: u32 = match proc.parse() {
|
||||||
|
Ok(i) => i,
|
||||||
|
_ => continue,
|
||||||
|
};
|
||||||
|
let mut fds = match fs::read_dir(path.join("fd")).await {
|
||||||
|
Ok(fds) => fds,
|
||||||
|
Err(e) => {
|
||||||
|
debug!("Process {proc} disappeared while scanning: {e}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
while let Ok(Some(f)) = fds.next_entry().await {
|
||||||
|
let path = match fs::read_link(f.path()).await {
|
||||||
|
Ok(p) => p,
|
||||||
|
Err(e) => {
|
||||||
|
debug!("Process {proc} disappeared while scanning: {e}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if path == hidraw {
|
||||||
|
let comm = match fs::read(format!("{}/proc/{proc}/comm", sysbase())).await {
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(e) => {
|
||||||
|
debug!("Process {proc} disappeared while scanning: {e}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if String::from_utf8_lossy(comm.as_ref()) == "steam\n" {
|
||||||
|
info!("Inhibiting hidraw{}", self.id);
|
||||||
|
self.inhibit().await?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
info!("Uninhibiting hidraw{}", self.id);
|
||||||
|
self.uninhibit().await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn inhibit(&self) -> Result<()> {
|
||||||
|
let mut res = Ok(());
|
||||||
|
for node in self.get_nodes().await?.into_iter() {
|
||||||
|
if let Err(err) = fs::write(node, "1\n").await {
|
||||||
|
error!("Encountered error inhibiting: {err}");
|
||||||
|
res = Err(err.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn uninhibit(&self) -> Result<()> {
|
||||||
|
let mut res = Ok(());
|
||||||
|
for node in self.get_nodes().await?.into_iter() {
|
||||||
|
if let Err(err) = fs::write(node, "0\n").await {
|
||||||
|
error!("Encountered error inhibiting: {err}");
|
||||||
|
res = Err(err.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Inhibitor {
|
||||||
|
pub fn new() -> Result<Inhibitor> {
|
||||||
|
let inotify = Inotify::init()?.into_event_stream([0; 512])?;
|
||||||
|
let dev_watch = inotify
|
||||||
|
.watches()
|
||||||
|
.add(sysbase() + "/dev", WatchMask::CREATE)?;
|
||||||
|
|
||||||
|
Ok(Inhibitor {
|
||||||
|
inotify,
|
||||||
|
dev_watch,
|
||||||
|
watches: HashMap::new(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn init() -> Result<Inhibitor> {
|
||||||
|
let mut inhibitor = match Inhibitor::new() {
|
||||||
|
Ok(i) => i,
|
||||||
|
Err(e) => {
|
||||||
|
error!("Could not create inotify watches: {e}");
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut dir = fs::read_dir(sysbase() + "/dev").await?;
|
||||||
|
while let Some(entry) = dir.next_entry().await? {
|
||||||
|
if let Err(e) = inhibitor.watch(entry.path().as_path()).await {
|
||||||
|
error!("Encountered error attempting to watch: {e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(inhibitor)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn watch(&mut self, path: &Path) -> Result<bool> {
|
||||||
|
let metadata = path.metadata()?;
|
||||||
|
if metadata.is_dir() {
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
let id = match path
|
||||||
|
.file_name()
|
||||||
|
.and_then(|f| f.to_str())
|
||||||
|
.and_then(|s| s.strip_prefix("hidraw"))
|
||||||
|
.and_then(|s| s.parse().ok())
|
||||||
|
{
|
||||||
|
Some(id) => id,
|
||||||
|
None => return Ok(false),
|
||||||
|
};
|
||||||
|
|
||||||
|
let node = HidNode::new(id);
|
||||||
|
if !node.can_inhibit().await {
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
info!("Adding {} to watchlist", path.display());
|
||||||
|
let watch = self.inotify.watches().add(
|
||||||
|
&node.hidraw(),
|
||||||
|
WatchMask::DELETE_SELF
|
||||||
|
| WatchMask::OPEN
|
||||||
|
| WatchMask::CLOSE_NOWRITE
|
||||||
|
| WatchMask::CLOSE_WRITE,
|
||||||
|
)?;
|
||||||
|
if let Err(e) = node.check().await {
|
||||||
|
error!(
|
||||||
|
"Encountered error attempting to check if hidraw{} can be inhibited: {e}",
|
||||||
|
node.id
|
||||||
|
);
|
||||||
|
}
|
||||||
|
self.watches.insert(watch, node);
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn process_event(&mut self, event: Event<OsString>) -> Result<()> {
|
||||||
|
const QSEC: Duration = Duration::from_millis(250);
|
||||||
|
debug!("Got event: {:08x}", event.mask);
|
||||||
|
if event.wd == self.dev_watch {
|
||||||
|
let path = match event.name {
|
||||||
|
Some(fname) => PathBuf::from(fname),
|
||||||
|
None => {
|
||||||
|
error!("Got an event without an associated filename!");
|
||||||
|
return Err(Error::msg("Got an event without an associated filename"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
debug!("New device {} found", path.display());
|
||||||
|
let path = PathBuf::from(sysbase() + "/dev").join(path);
|
||||||
|
sleep(QSEC); // Wait a quarter second for nodes to enumerate
|
||||||
|
if let Err(e) = self.watch(path.as_path()).await {
|
||||||
|
error!("Encountered error attempting to watch: {e}");
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
} else if event.mask == EventMask::DELETE_SELF {
|
||||||
|
debug!("Device removed");
|
||||||
|
self.watches.remove(&event.wd);
|
||||||
|
let _ = self.inotify.watches().remove(event.wd);
|
||||||
|
} else if let Some(node) = self.watches.get(&event.wd) {
|
||||||
|
node.check().await?;
|
||||||
|
} else if event.mask != EventMask::IGNORED {
|
||||||
|
error!("Unhandled event: {:08x}", event.mask);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Service for Inhibitor {
|
||||||
|
const NAME: &'static str = "ds-inhibitor";
|
||||||
|
|
||||||
|
async fn run(&mut self) -> Result<()> {
|
||||||
|
loop {
|
||||||
|
let res = match self.inotify.next().await {
|
||||||
|
Some(Ok(event)) => self.process_event(event).await,
|
||||||
|
Some(Err(e)) => return Err(e.into()),
|
||||||
|
None => return Ok(()),
|
||||||
|
};
|
||||||
|
if let Err(e) = res {
|
||||||
|
warn!("Got error processing event: {e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn shutdown(&mut self) -> Result<()> {
|
||||||
|
let mut res = Ok(());
|
||||||
|
for (wd, node) in self.watches.drain() {
|
||||||
|
if let Err(e) = self.inotify.watches().remove(wd) {
|
||||||
|
warn!("Error removing watch while shutting down: {e}");
|
||||||
|
res = Err(e.into());
|
||||||
|
}
|
||||||
|
if let Err(e) = node.uninhibit().await {
|
||||||
|
warn!("Error uninhibiting {} while shutting down: {e}", node.id);
|
||||||
|
res = Err(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use crate::testing;
|
||||||
|
use std::fs::{create_dir_all, read_to_string, remove_file, write, File};
|
||||||
|
use std::os::unix::fs::symlink;
|
||||||
|
use tokio::time::sleep;
|
||||||
|
|
||||||
|
async fn nyield(times: u32) {
|
||||||
|
for i in 0..times {
|
||||||
|
sleep(Duration::from_millis(1)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn hid_nodes() {
|
||||||
|
let h = testing::start();
|
||||||
|
let path = h.test.path();
|
||||||
|
|
||||||
|
let hid = HidNode::new(0);
|
||||||
|
let sys_base = hid.sys_base();
|
||||||
|
|
||||||
|
create_dir_all(sys_base.join("input/input0/foo0")).expect("foo0");
|
||||||
|
create_dir_all(sys_base.join("input/input1/bar0")).expect("bar0");
|
||||||
|
create_dir_all(sys_base.join("input/input2/mouse0")).expect("mouse0");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
hid.get_nodes().await.expect("get_nodes"),
|
||||||
|
&[sys_base.join("input/input2/inhibited")]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn hid_can_inhibit() {
|
||||||
|
let h = testing::start();
|
||||||
|
let path = h.test.path();
|
||||||
|
|
||||||
|
let hids = [
|
||||||
|
HidNode::new(0),
|
||||||
|
HidNode::new(1),
|
||||||
|
HidNode::new(2),
|
||||||
|
HidNode::new(3),
|
||||||
|
HidNode::new(4),
|
||||||
|
HidNode::new(5),
|
||||||
|
HidNode::new(6),
|
||||||
|
];
|
||||||
|
|
||||||
|
create_dir_all(hids[0].sys_base().join("input/input0/foo0")).expect("foo0");
|
||||||
|
symlink("foo", hids[0].sys_base().join("driver")).expect("hidraw0");
|
||||||
|
create_dir_all(hids[1].sys_base().join("input/input1/mouse0")).expect("mouse0");
|
||||||
|
symlink("foo", hids[1].sys_base().join("driver")).expect("hidraw1");
|
||||||
|
create_dir_all(hids[2].sys_base().join("input/input2/foo1")).expect("foo1");
|
||||||
|
symlink("sony", hids[2].sys_base().join("driver")).expect("hidraw2");
|
||||||
|
create_dir_all(hids[3].sys_base().join("input/input3/mouse1")).expect("mouse1");
|
||||||
|
symlink("sony", hids[3].sys_base().join("driver")).expect("hidraw3");
|
||||||
|
create_dir_all(hids[4].sys_base().join("input/input4/foo2")).expect("foo2");
|
||||||
|
symlink("playstation", hids[4].sys_base().join("driver")).expect("hidraw4");
|
||||||
|
create_dir_all(hids[5].sys_base().join("input/input5/mouse2")).expect("mouse2");
|
||||||
|
symlink("playstation", hids[5].sys_base().join("driver")).expect("hidraw5");
|
||||||
|
create_dir_all(hids[6].sys_base().join("input/input6/mouse3")).expect("mouse3");
|
||||||
|
|
||||||
|
assert!(!hids[0].can_inhibit().await);
|
||||||
|
assert!(!hids[1].can_inhibit().await);
|
||||||
|
assert!(!hids[2].can_inhibit().await);
|
||||||
|
assert!(hids[3].can_inhibit().await);
|
||||||
|
assert!(!hids[4].can_inhibit().await);
|
||||||
|
assert!(hids[5].can_inhibit().await);
|
||||||
|
assert!(!hids[6].can_inhibit().await);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn hid_inhibit() {
|
||||||
|
let h = testing::start();
|
||||||
|
let path = h.test.path();
|
||||||
|
|
||||||
|
let hid = HidNode::new(0);
|
||||||
|
let sys_base = hid.sys_base();
|
||||||
|
|
||||||
|
create_dir_all(sys_base.join("input/input0/mouse0")).expect("mouse0");
|
||||||
|
symlink("sony", sys_base.join("driver")).expect("hidraw0");
|
||||||
|
|
||||||
|
assert!(hid.can_inhibit().await);
|
||||||
|
|
||||||
|
hid.inhibit().await;
|
||||||
|
assert_eq!(
|
||||||
|
read_to_string(sys_base.join("input/input0/inhibited")).expect("inhibited"),
|
||||||
|
"1\n"
|
||||||
|
);
|
||||||
|
hid.uninhibit().await;
|
||||||
|
assert_eq!(
|
||||||
|
read_to_string(sys_base.join("input/input0/inhibited")).expect("inhibited"),
|
||||||
|
"0\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn hid_inhibit_error_continue() {
|
||||||
|
let h = testing::start();
|
||||||
|
let path = h.test.path();
|
||||||
|
|
||||||
|
let hid = HidNode::new(0);
|
||||||
|
let sys_base = hid.sys_base();
|
||||||
|
|
||||||
|
create_dir_all(sys_base.join("input/input0/mouse0")).expect("mouse0");
|
||||||
|
create_dir_all(sys_base.join("input/input0/inhibited")).expect("inhibited");
|
||||||
|
create_dir_all(sys_base.join("input/input1/mouse1")).expect("mouse0");
|
||||||
|
symlink("sony", sys_base.join("driver")).expect("hidraw0");
|
||||||
|
|
||||||
|
assert!(hid.can_inhibit().await);
|
||||||
|
|
||||||
|
hid.inhibit().await;
|
||||||
|
assert_eq!(
|
||||||
|
read_to_string(sys_base.join("input/input1/inhibited")).expect("inhibited"),
|
||||||
|
"1\n"
|
||||||
|
);
|
||||||
|
hid.uninhibit().await;
|
||||||
|
assert_eq!(
|
||||||
|
read_to_string(sys_base.join("input/input1/inhibited")).expect("inhibited"),
|
||||||
|
"0\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn hid_check() {
|
||||||
|
let h = testing::start();
|
||||||
|
let path = h.test.path();
|
||||||
|
|
||||||
|
let hid = HidNode::new(0);
|
||||||
|
let sys_base = hid.sys_base();
|
||||||
|
|
||||||
|
create_dir_all(sys_base.join("input/input0/mouse0")).expect("mouse0");
|
||||||
|
symlink("sony", sys_base.join("driver")).expect("hidraw0");
|
||||||
|
create_dir_all(path.join("proc/1/fd")).expect("fd");
|
||||||
|
|
||||||
|
symlink(hid.hidraw(), path.join("proc/1/fd/3")).expect("symlink");
|
||||||
|
write(path.join("proc/1/comm"), "steam\n").expect("comm");
|
||||||
|
|
||||||
|
hid.check().await.expect("check");
|
||||||
|
assert_eq!(
|
||||||
|
read_to_string(sys_base.join("input/input0/inhibited")).expect("inhibited"),
|
||||||
|
"1\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
write(path.join("proc/1/comm"), "epic\n").expect("comm");
|
||||||
|
hid.check().await.expect("check");
|
||||||
|
assert_eq!(
|
||||||
|
read_to_string(sys_base.join("input/input0/inhibited")).expect("inhibited"),
|
||||||
|
"0\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
remove_file(path.join("proc/1/fd/3")).expect("rm");
|
||||||
|
write(path.join("proc/1/comm"), "steam\n").expect("comm");
|
||||||
|
hid.check().await.expect("check");
|
||||||
|
assert_eq!(
|
||||||
|
read_to_string(sys_base.join("input/input0/inhibited")).expect("inhibited"),
|
||||||
|
"0\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn inhibitor_start() {
|
||||||
|
let h = testing::start();
|
||||||
|
let path = h.test.path();
|
||||||
|
|
||||||
|
let hid = HidNode::new(0);
|
||||||
|
let sys_base = hid.sys_base();
|
||||||
|
|
||||||
|
create_dir_all(path.join("dev")).expect("dev");
|
||||||
|
create_dir_all(sys_base.join("input/input0/mouse0")).expect("mouse0");
|
||||||
|
write(hid.hidraw(), "").expect("hidraw");
|
||||||
|
symlink("sony", sys_base.join("driver")).expect("driver");
|
||||||
|
create_dir_all(path.join("proc/1/fd")).expect("fd");
|
||||||
|
symlink(hid.hidraw(), path.join("proc/1/fd/3")).expect("symlink");
|
||||||
|
write(path.join("proc/1/comm"), "steam\n").expect("comm");
|
||||||
|
|
||||||
|
let mut inhibitor = Inhibitor::init().await.expect("init");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
read_to_string(sys_base.join("input/input0/inhibited")).expect("inhibited"),
|
||||||
|
"1\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
inhibitor.shutdown().await.expect("stop");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
read_to_string(sys_base.join("input/input0/inhibited")).expect("inhibited"),
|
||||||
|
"0\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn inhibitor_open_close() {
|
||||||
|
let h = testing::start();
|
||||||
|
let path = h.test.path();
|
||||||
|
|
||||||
|
let hid = HidNode::new(0);
|
||||||
|
let sys_base = hid.sys_base();
|
||||||
|
|
||||||
|
create_dir_all(path.join("dev")).expect("dev");
|
||||||
|
create_dir_all(sys_base.join("input/input0/mouse0")).expect("mouse0");
|
||||||
|
File::create(hid.hidraw()).expect("hidraw");
|
||||||
|
symlink("sony", sys_base.join("driver")).expect("driver");
|
||||||
|
create_dir_all(path.join("proc/1/fd")).expect("fd");
|
||||||
|
write(path.join("proc/1/comm"), "steam\n").expect("comm");
|
||||||
|
|
||||||
|
let mut inhibitor = Inhibitor::init().await.expect("init");
|
||||||
|
let task = tokio::spawn(async move {
|
||||||
|
inhibitor.run().await;
|
||||||
|
});
|
||||||
|
|
||||||
|
nyield(1).await;
|
||||||
|
assert_eq!(
|
||||||
|
read_to_string(sys_base.join("input/input0/inhibited")).expect("inhibited"),
|
||||||
|
"0\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
symlink(hid.hidraw(), path.join("proc/1/fd/3")).expect("symlink");
|
||||||
|
let f = File::open(hid.hidraw()).expect("hidraw");
|
||||||
|
nyield(2).await;
|
||||||
|
assert_eq!(
|
||||||
|
read_to_string(sys_base.join("input/input0/inhibited")).expect("inhibited"),
|
||||||
|
"1\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
drop(f);
|
||||||
|
remove_file(path.join("proc/1/fd/3")).expect("rm");
|
||||||
|
nyield(1).await;
|
||||||
|
assert_eq!(
|
||||||
|
read_to_string(sys_base.join("input/input0/inhibited")).expect("inhibited"),
|
||||||
|
"0\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
task.abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn inhibitor_fast_create() {
|
||||||
|
let h = testing::start();
|
||||||
|
let path = h.test.path();
|
||||||
|
|
||||||
|
let hid = HidNode::new(0);
|
||||||
|
let sys_base = hid.sys_base();
|
||||||
|
|
||||||
|
create_dir_all(path.join("dev")).expect("dev");
|
||||||
|
create_dir_all(sys_base.join("input/input0/mouse0")).expect("mouse0");
|
||||||
|
symlink("sony", sys_base.join("driver")).expect("driver");
|
||||||
|
create_dir_all(path.join("proc/1/fd")).expect("fd");
|
||||||
|
write(path.join("proc/1/comm"), "steam\n").expect("comm");
|
||||||
|
|
||||||
|
let mut inhibitor = Inhibitor::init().await.expect("init");
|
||||||
|
let task = tokio::spawn(async move {
|
||||||
|
inhibitor.run().await;
|
||||||
|
});
|
||||||
|
|
||||||
|
nyield(1).await;
|
||||||
|
assert!(read_to_string(sys_base.join("input/input0/inhibited")).is_err());
|
||||||
|
|
||||||
|
File::create(hid.hidraw()).expect("hidraw");
|
||||||
|
symlink(hid.hidraw(), path.join("proc/1/fd/3")).expect("symlink");
|
||||||
|
let f = File::open(hid.hidraw()).expect("hidraw");
|
||||||
|
nyield(4).await;
|
||||||
|
assert_eq!(
|
||||||
|
read_to_string(sys_base.join("input/input0/inhibited")).expect("inhibited"),
|
||||||
|
"1\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
task.abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn inhibitor_create() {
|
||||||
|
let h = testing::start();
|
||||||
|
let path = h.test.path();
|
||||||
|
|
||||||
|
let hid = HidNode::new(0);
|
||||||
|
let sys_base = hid.sys_base();
|
||||||
|
|
||||||
|
create_dir_all(path.join("dev")).expect("dev");
|
||||||
|
create_dir_all(sys_base.join("input/input0/mouse0")).expect("mouse0");
|
||||||
|
symlink("sony", sys_base.join("driver")).expect("driver");
|
||||||
|
create_dir_all(path.join("proc/1/fd")).expect("fd");
|
||||||
|
write(path.join("proc/1/comm"), "steam\n").expect("comm");
|
||||||
|
|
||||||
|
let mut inhibitor = Inhibitor::init().await.expect("init");
|
||||||
|
let task = tokio::spawn(async move {
|
||||||
|
inhibitor.run().await;
|
||||||
|
});
|
||||||
|
|
||||||
|
nyield(3).await;
|
||||||
|
assert!(read_to_string(sys_base.join("input/input0/inhibited")).is_err());
|
||||||
|
|
||||||
|
File::create(hid.hidraw()).expect("hidraw");
|
||||||
|
nyield(3).await;
|
||||||
|
symlink(hid.hidraw(), path.join("proc/1/fd/3")).expect("symlink");
|
||||||
|
let f = File::open(hid.hidraw()).expect("hidraw");
|
||||||
|
nyield(3).await;
|
||||||
|
assert_eq!(
|
||||||
|
read_to_string(sys_base.join("input/input0/inhibited")).expect("inhibited"),
|
||||||
|
"1\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
task.abort();
|
||||||
|
}
|
||||||
|
}
|
236
src/main.rs
236
src/main.rs
|
@ -24,32 +24,244 @@
|
||||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use anyhow::{Error, Result};
|
use anyhow::{bail, Error, Result};
|
||||||
use tokio::signal::unix::{signal, SignalKind};
|
use tokio::signal::unix::{signal, SignalKind};
|
||||||
use tracing_subscriber;
|
use tokio::task::JoinSet;
|
||||||
|
use tokio_util::sync::CancellationToken;
|
||||||
|
use tracing::{error, info, warn};
|
||||||
|
use tracing_subscriber::prelude::*;
|
||||||
|
use tracing_subscriber::{fmt, Registry};
|
||||||
|
use zbus::connection::Connection;
|
||||||
use zbus::ConnectionBuilder;
|
use zbus::ConnectionBuilder;
|
||||||
|
|
||||||
|
use crate::ds_inhibit::Inhibitor;
|
||||||
|
use crate::sls::ftrace::Ftrace;
|
||||||
|
use crate::sls::{LogLayer, LogReceiver};
|
||||||
|
|
||||||
|
mod ds_inhibit;
|
||||||
mod manager;
|
mod manager;
|
||||||
|
mod sls;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod testing;
|
||||||
|
|
||||||
|
trait Service
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
const NAME: &'static str;
|
||||||
|
|
||||||
|
async fn run(&mut self) -> Result<()>;
|
||||||
|
|
||||||
|
async fn shutdown(&mut self) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn start(mut self, token: CancellationToken) -> Result<()> {
|
||||||
|
info!("Starting {}", Self::NAME);
|
||||||
|
let res = tokio::select! {
|
||||||
|
r = self.run() => r,
|
||||||
|
_ = token.cancelled() => Ok(()),
|
||||||
|
};
|
||||||
|
if res.is_err() {
|
||||||
|
warn!(
|
||||||
|
"{} encountered an error: {}",
|
||||||
|
Self::NAME,
|
||||||
|
res.as_ref().unwrap_err()
|
||||||
|
);
|
||||||
|
token.cancel();
|
||||||
|
}
|
||||||
|
info!("Shutting down {}", Self::NAME);
|
||||||
|
self.shutdown().await.and(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(test))]
|
||||||
|
pub fn sysbase() -> String {
|
||||||
|
String::new()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub fn sysbase() -> String {
|
||||||
|
let current_test = crate::testing::current();
|
||||||
|
let path = current_test.path();
|
||||||
|
String::from(path.as_os_str().to_str().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_comm(pid: u32) -> Result<String> {
|
||||||
|
let comm = std::fs::read_to_string(format!("{}/proc/{}/comm", sysbase(), pid))?;
|
||||||
|
Ok(comm.trim_end().to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_appid(pid: u32) -> Result<Option<u64>> {
|
||||||
|
let environ = std::fs::read_to_string(format!("{}/proc/{}/environ", sysbase(), pid))?;
|
||||||
|
for env_var in environ.split('\0') {
|
||||||
|
let (key, value) = match env_var.split_once('=') {
|
||||||
|
Some((k, v)) => (k, v),
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
if key != "SteamGameId" {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
match value.parse() {
|
||||||
|
Ok(appid) => return Ok(Some(appid)),
|
||||||
|
Err(_) => break,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let stat = std::fs::read_to_string(format!("{}/proc/{}/stat", sysbase(), pid))?;
|
||||||
|
let stat = match stat.rsplit_once(") ") {
|
||||||
|
Some((_, v)) => v,
|
||||||
|
None => return Ok(None),
|
||||||
|
};
|
||||||
|
let ppid = match stat.split(' ').nth(1) {
|
||||||
|
Some(ppid) => ppid,
|
||||||
|
None => return Err(anyhow::Error::msg("stat data invalid")),
|
||||||
|
};
|
||||||
|
let ppid: u32 = ppid.parse()?;
|
||||||
|
if ppid > 1 {
|
||||||
|
get_appid(ppid)
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn reload() -> Result<()> {
|
||||||
|
loop {
|
||||||
|
let mut sighup = signal(SignalKind::hangup())?;
|
||||||
|
sighup.recv().await.ok_or(Error::msg(""))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_connection() -> Result<Connection> {
|
||||||
|
let manager = manager::SMManager::new()?;
|
||||||
|
|
||||||
|
ConnectionBuilder::system()?
|
||||||
|
.name("com.steampowered.SteamOSManager1")?
|
||||||
|
.serve_at("/com/steampowered/SteamOSManager1", manager)?
|
||||||
|
.build()
|
||||||
|
.await
|
||||||
|
.map_err(|e| e.into())
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
// This daemon is responsible for creating a dbus api that steam client can use to do various OS
|
// This daemon is responsible for creating a dbus api that steam client can use to do various OS
|
||||||
// level things. It implements com.steampowered.SteamOSManager1 interface
|
// level things. It implements com.steampowered.SteamOSManager1 interface
|
||||||
|
|
||||||
tracing_subscriber::fmt::init();
|
let stdout_log = fmt::layer();
|
||||||
|
let subscriber = Registry::default().with(stdout_log);
|
||||||
|
|
||||||
|
let connection = match create_connection().await {
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(e) => {
|
||||||
|
let _guard = tracing::subscriber::set_default(subscriber);
|
||||||
|
error!("Error connecting to DBus: {}", e);
|
||||||
|
bail!(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut services = JoinSet::new();
|
||||||
|
let token = CancellationToken::new();
|
||||||
|
|
||||||
|
let mut log_receiver = LogReceiver::new(connection.clone()).await?;
|
||||||
|
let remote_logger = LogLayer::new(&log_receiver).await;
|
||||||
|
let subscriber = subscriber.with(remote_logger);
|
||||||
|
let _guard = tracing::subscriber::set_global_default(subscriber)?;
|
||||||
|
|
||||||
let mut sigterm = signal(SignalKind::terminate())?;
|
let mut sigterm = signal(SignalKind::terminate())?;
|
||||||
|
let mut sigquit = signal(SignalKind::quit())?;
|
||||||
|
|
||||||
let manager = manager::SMManager::new()?;
|
let ftrace = Ftrace::init(connection.clone()).await?;
|
||||||
|
services.spawn(ftrace.start(token.clone()));
|
||||||
|
|
||||||
let _system_connection = ConnectionBuilder::system()?
|
let inhibitor = Inhibitor::init().await?;
|
||||||
.name("com.steampowered.SteamOSManager1")?
|
services.spawn(inhibitor.start(token.clone()));
|
||||||
.serve_at("/com/steampowered/SteamOSManager1", manager)?
|
|
||||||
.build()
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
tokio::select! {
|
let mut res = tokio::select! {
|
||||||
e = sigterm.recv() => e.ok_or(Error::msg("SIGTERM pipe broke")),
|
e = log_receiver.run() => e,
|
||||||
e = tokio::signal::ctrl_c() => Ok(e?),
|
e = services.join_next() => match e.unwrap() {
|
||||||
|
Ok(Ok(())) => Ok(()),
|
||||||
|
Ok(Err(e)) => Err(e),
|
||||||
|
Err(e) => Err(e.into())
|
||||||
|
},
|
||||||
|
_ = tokio::signal::ctrl_c() => Ok(()),
|
||||||
|
e = sigterm.recv() => e.ok_or(Error::msg("SIGTERM machine broke")),
|
||||||
|
_ = sigquit.recv() => Err(Error::msg("Got SIGQUIT")),
|
||||||
|
e = reload() => e,
|
||||||
|
}
|
||||||
|
.inspect_err(|e| error!("Encountered error running: {e}"));
|
||||||
|
token.cancel();
|
||||||
|
|
||||||
|
info!("Shutting down");
|
||||||
|
|
||||||
|
while let Some(service_res) = services.join_next().await {
|
||||||
|
res = match service_res {
|
||||||
|
Ok(Err(e)) => Err(e),
|
||||||
|
Err(e) => Err(e.into()),
|
||||||
|
_ => continue,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
res.inspect_err(|e| error!("Encountered error: {e}"))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use crate::testing;
|
||||||
|
use std::fs;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn read_comm() {
|
||||||
|
let h = testing::start();
|
||||||
|
let path = h.test.path();
|
||||||
|
fs::create_dir_all(path.join("proc/123456")).expect("create_dir_all");
|
||||||
|
fs::write(path.join("proc/123456/comm"), "test\n").expect("write comm");
|
||||||
|
|
||||||
|
assert_eq!(crate::read_comm(123456).expect("read_comm"), "test");
|
||||||
|
assert!(crate::read_comm(123457).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn appid_environ() {
|
||||||
|
let h = testing::start();
|
||||||
|
let path = h.test.path();
|
||||||
|
fs::create_dir_all(path.join("proc/123456")).expect("create_dir_all");
|
||||||
|
fs::write(
|
||||||
|
path.join("proc/123456/environ"),
|
||||||
|
"A=B\0SteamGameId=98765\0C=D",
|
||||||
|
)
|
||||||
|
.expect("write environ");
|
||||||
|
|
||||||
|
assert_eq!(crate::get_appid(123456).expect("get_appid"), Some(98765));
|
||||||
|
assert!(crate::get_appid(123457).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn appid_parent_environ() {
|
||||||
|
let h = testing::start();
|
||||||
|
let path = h.test.path();
|
||||||
|
fs::create_dir_all(path.join("proc/123456")).expect("create_dir_all");
|
||||||
|
fs::write(
|
||||||
|
path.join("proc/123456/environ"),
|
||||||
|
"A=B\0SteamGameId=98765\0C=D",
|
||||||
|
)
|
||||||
|
.expect("write environ");
|
||||||
|
fs::create_dir_all(path.join("proc/123457")).expect("create_dir_all");
|
||||||
|
fs::write(path.join("proc/123457/environ"), "A=B\0C=D").expect("write environ");
|
||||||
|
fs::write(path.join("proc/123457/stat"), "0 (comm) S 123456 ...").expect("write stat");
|
||||||
|
|
||||||
|
assert_eq!(crate::get_appid(123457).expect("get_appid"), Some(98765));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn appid_missing() {
|
||||||
|
let h = testing::start();
|
||||||
|
let path = h.test.path();
|
||||||
|
fs::create_dir_all(path.join("proc/123457")).expect("create_dir_all");
|
||||||
|
fs::write(path.join("proc/123457/environ"), "A=B\0C=D").expect("write environ");
|
||||||
|
fs::write(path.join("proc/123457/stat"), "0 (comm) S 1 ...").expect("write stat");
|
||||||
|
|
||||||
|
assert_eq!(crate::get_appid(123457).expect("get_appid"), None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
286
src/sls/ftrace.rs
Normal file
286
src/sls/ftrace.rs
Normal file
|
@ -0,0 +1,286 @@
|
||||||
|
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||||
|
use anyhow::{Error, Result};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use std::path::Path;
|
||||||
|
use tokio::fs;
|
||||||
|
use tokio::io::{AsyncBufReadExt, BufReader};
|
||||||
|
use tokio::net::unix::pipe;
|
||||||
|
use tracing::{error, info};
|
||||||
|
use zbus::connection::Connection;
|
||||||
|
use zbus::zvariant;
|
||||||
|
|
||||||
|
use crate::{get_appid, read_comm, sysbase, Service};
|
||||||
|
|
||||||
|
#[zbus::proxy(
|
||||||
|
interface = "com.steampowered.SteamOSLogSubmitter.Trace",
|
||||||
|
default_service = "com.steampowered.SteamOSLogSubmitter",
|
||||||
|
default_path = "/com/steampowered/SteamOSLogSubmitter/helpers/Trace"
|
||||||
|
)]
|
||||||
|
trait TraceHelper {
|
||||||
|
async fn log_event(
|
||||||
|
&self,
|
||||||
|
trace: &str,
|
||||||
|
data: HashMap<&str, zvariant::Value<'_>>,
|
||||||
|
) -> zbus::Result<()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Ftrace
|
||||||
|
where
|
||||||
|
Self: 'static,
|
||||||
|
{
|
||||||
|
pipe: Option<BufReader<pipe::Receiver>>,
|
||||||
|
proxy: TraceHelperProxy<'static>,
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn setup_traces(path: &Path) -> Result<()> {
|
||||||
|
fs::write(path.join("events/oom/mark_victim/enable"), "1").await?;
|
||||||
|
fs::write(path.join("set_ftrace_filter"), "split_lock_warn").await?;
|
||||||
|
fs::write(path.join("current_tracer"), "function").await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ftrace {
|
||||||
|
pub async fn init(connection: Connection) -> Result<Ftrace> {
|
||||||
|
let base = Self::base();
|
||||||
|
let path = Path::new(base.as_str());
|
||||||
|
fs::create_dir_all(path).await?;
|
||||||
|
setup_traces(path).await?;
|
||||||
|
let file = pipe::OpenOptions::new()
|
||||||
|
.unchecked(true) // Thanks tracefs for making trace_pipe a "regular" file
|
||||||
|
.open_receiver(path.join("trace_pipe"))?;
|
||||||
|
Ok(Ftrace {
|
||||||
|
pipe: Some(BufReader::new(file)),
|
||||||
|
proxy: TraceHelperProxy::new(&connection).await?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn base() -> String {
|
||||||
|
sysbase() + "/sys/kernel/tracing/instances/steamos-log-submitter"
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_pid(data: &mut HashMap<&str, zvariant::Value<'_>>, pid: u32) -> Result<()> {
|
||||||
|
if let Ok(comm) = read_comm(pid) {
|
||||||
|
info!("├─ comm: {}", comm);
|
||||||
|
data.insert("comm", zvariant::Value::new(comm));
|
||||||
|
} else {
|
||||||
|
info!("├─ comm not found");
|
||||||
|
}
|
||||||
|
if let Ok(Some(appid)) = get_appid(pid) {
|
||||||
|
info!("└─ appid: {}", appid);
|
||||||
|
data.insert("appid", zvariant::Value::new(appid));
|
||||||
|
} else {
|
||||||
|
info!("└─ appid not found");
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_event(&mut self, line: &str) -> Result<()> {
|
||||||
|
info!("Forwarding line {}", line);
|
||||||
|
let mut data = HashMap::new();
|
||||||
|
let mut split = line.rsplit(' ');
|
||||||
|
if let Some(("pid", pid)) = split.next().and_then(|arg| arg.split_once('=')) {
|
||||||
|
let pid = pid.parse()?;
|
||||||
|
Ftrace::handle_pid(&mut data, pid).await?;
|
||||||
|
}
|
||||||
|
self.proxy.log_event(line, data).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Service for Ftrace {
|
||||||
|
const NAME: &'static str = "ftrace";
|
||||||
|
|
||||||
|
async fn run(&mut self) -> Result<()> {
|
||||||
|
loop {
|
||||||
|
let mut string = String::new();
|
||||||
|
self.pipe
|
||||||
|
.as_mut()
|
||||||
|
.ok_or(Error::msg("BUG: trace_pipe missing"))?
|
||||||
|
.read_line(&mut string)
|
||||||
|
.await?;
|
||||||
|
if let Err(e) = self.handle_event(string.trim_end()).await {
|
||||||
|
error!("Encountered an error handling event: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn shutdown(&mut self) -> Result<()> {
|
||||||
|
self.pipe.take();
|
||||||
|
fs::remove_dir(Self::base()).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use crate::testing;
|
||||||
|
use nix::sys::stat::Mode;
|
||||||
|
use nix::unistd;
|
||||||
|
use std::cell::Cell;
|
||||||
|
use std::fs;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};
|
||||||
|
|
||||||
|
struct MockTrace {
|
||||||
|
traces: UnboundedSender<(String, HashMap<String, zvariant::OwnedValue>)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[zbus::interface(name = "com.steampowered.SteamOSLogSubmitter.Trace")]
|
||||||
|
impl MockTrace {
|
||||||
|
fn log_event(
|
||||||
|
&mut self,
|
||||||
|
trace: &str,
|
||||||
|
data: HashMap<&str, zvariant::Value<'_>>,
|
||||||
|
) -> zbus::fdo::Result<()> {
|
||||||
|
self.traces.send((
|
||||||
|
String::from(trace),
|
||||||
|
HashMap::from_iter(
|
||||||
|
data.iter()
|
||||||
|
.map(|(k, v)| (String::from(*k), v.try_to_owned().unwrap())),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn handle_pid() {
|
||||||
|
let h = testing::start();
|
||||||
|
let path = h.test.path();
|
||||||
|
|
||||||
|
fs::create_dir_all(path.join("proc/1234")).expect("create_dir_all");
|
||||||
|
fs::write(path.join("proc/1234/comm"), "ftrace\n").expect("write comm");
|
||||||
|
fs::write(path.join("proc/1234/environ"), "SteamGameId=5678").expect("write environ");
|
||||||
|
|
||||||
|
fs::create_dir_all(path.join("proc/1235")).expect("create_dir_all");
|
||||||
|
fs::write(path.join("proc/1235/comm"), "ftrace\n").expect("write comm");
|
||||||
|
|
||||||
|
fs::create_dir_all(path.join("proc/1236")).expect("create_dir_all");
|
||||||
|
fs::write(path.join("proc/1236/environ"), "SteamGameId=5678").expect("write environ");
|
||||||
|
|
||||||
|
let mut map = HashMap::new();
|
||||||
|
assert!(Ftrace::handle_pid(&mut map, 1234).await.is_ok());
|
||||||
|
assert_eq!(
|
||||||
|
*map.get("comm").expect("comm"),
|
||||||
|
zvariant::Value::new("ftrace")
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
*map.get("appid").expect("appid"),
|
||||||
|
zvariant::Value::new(5678 as u64)
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut map = HashMap::new();
|
||||||
|
assert!(Ftrace::handle_pid(&mut map, 1235).await.is_ok());
|
||||||
|
assert_eq!(
|
||||||
|
*map.get("comm").expect("comm"),
|
||||||
|
zvariant::Value::new("ftrace")
|
||||||
|
);
|
||||||
|
assert!(map.get("appid").is_none());
|
||||||
|
|
||||||
|
let mut map = HashMap::new();
|
||||||
|
assert!(Ftrace::handle_pid(&mut map, 1236).await.is_ok());
|
||||||
|
assert!(map.get("comm").is_none());
|
||||||
|
assert_eq!(
|
||||||
|
*map.get("appid").expect("appid"),
|
||||||
|
zvariant::Value::new(5678 as u64)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn ftrace_init() {
|
||||||
|
let h = testing::start();
|
||||||
|
let path = h.test.path();
|
||||||
|
|
||||||
|
let tracefs = PathBuf::from(Ftrace::base());
|
||||||
|
|
||||||
|
fs::create_dir_all(tracefs.join("events/oom/mark_victim")).expect("create_dir_all");
|
||||||
|
unistd::mkfifo(
|
||||||
|
tracefs.join("trace_pipe").as_path(),
|
||||||
|
Mode::S_IRUSR | Mode::S_IWUSR,
|
||||||
|
)
|
||||||
|
.expect("trace_pipe");
|
||||||
|
let dbus = Connection::session().await.expect("dbus");
|
||||||
|
let ftrace = Ftrace::init(dbus).await.expect("ftrace");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
fs::read_to_string(tracefs.join("events/oom/mark_victim/enable")).unwrap(),
|
||||||
|
"1"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn ftrace_relay() {
|
||||||
|
let h = testing::start();
|
||||||
|
let path = h.test.path();
|
||||||
|
|
||||||
|
let tracefs = PathBuf::from(Ftrace::base());
|
||||||
|
|
||||||
|
fs::create_dir_all(tracefs.join("events/oom/mark_victim")).expect("create_dir_all");
|
||||||
|
unistd::mkfifo(
|
||||||
|
tracefs.join("trace_pipe").as_path(),
|
||||||
|
Mode::S_IRUSR | Mode::S_IWUSR,
|
||||||
|
)
|
||||||
|
.expect("trace_pipe");
|
||||||
|
|
||||||
|
fs::create_dir_all(path.join("proc/14351")).expect("create_dir_all");
|
||||||
|
fs::write(path.join("proc/14351/comm"), "ftrace\n").expect("write comm");
|
||||||
|
fs::write(path.join("proc/14351/environ"), "SteamGameId=5678").expect("write environ");
|
||||||
|
|
||||||
|
let (sender, mut receiver) = unbounded_channel();
|
||||||
|
let trace = MockTrace { traces: sender };
|
||||||
|
let dbus = zbus::connection::Builder::session()
|
||||||
|
.unwrap()
|
||||||
|
.name("com.steampowered.SteamOSLogSubmitter")
|
||||||
|
.unwrap()
|
||||||
|
.serve_at("/com/steampowered/SteamOSLogSubmitter/helpers/Trace", trace)
|
||||||
|
.unwrap()
|
||||||
|
.build()
|
||||||
|
.await
|
||||||
|
.expect("dbus");
|
||||||
|
let mut ftrace = Ftrace::init(dbus).await.expect("ftrace");
|
||||||
|
|
||||||
|
assert!(match receiver.try_recv() {
|
||||||
|
Empty => true,
|
||||||
|
_ => false,
|
||||||
|
});
|
||||||
|
ftrace
|
||||||
|
.handle_event(
|
||||||
|
" GamepadUI Input-4886 [003] .N.1. 23828.572941: mark_victim: pid=14351",
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.expect("event");
|
||||||
|
let (line, data) = match receiver.try_recv() {
|
||||||
|
Ok((line, data)) => (line, data),
|
||||||
|
_ => panic!("Test failed"),
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
line,
|
||||||
|
" GamepadUI Input-4886 [003] .N.1. 23828.572941: mark_victim: pid=14351"
|
||||||
|
);
|
||||||
|
assert_eq!(data.len(), 2);
|
||||||
|
assert_eq!(
|
||||||
|
data.get("appid").map(|v| v.downcast_ref()),
|
||||||
|
Some(Ok(5678 as u64))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
data.get("comm").map(|v| v.downcast_ref()),
|
||||||
|
Some(Ok("ftrace"))
|
||||||
|
);
|
||||||
|
|
||||||
|
ftrace
|
||||||
|
.handle_event(" GamepadUI Input-4886 [003] .N.1. 23828.572941: split_lock_warn <-")
|
||||||
|
.await
|
||||||
|
.expect("event");
|
||||||
|
let (line, data) = match receiver.try_recv() {
|
||||||
|
Ok((line, data)) => (line, data),
|
||||||
|
_ => panic!("Test failed"),
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
line,
|
||||||
|
" GamepadUI Input-4886 [003] .N.1. 23828.572941: split_lock_warn <-"
|
||||||
|
);
|
||||||
|
assert_eq!(data.len(), 0);
|
||||||
|
}
|
||||||
|
}
|
137
src/sls/mod.rs
Normal file
137
src/sls/mod.rs
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
/* SPDX-License-Identifier: BSD-2-Clause */
|
||||||
|
pub mod ftrace;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use std::time::SystemTime;
|
||||||
|
use tokio::sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender};
|
||||||
|
use tracing::field::{Field, Visit};
|
||||||
|
use tracing::{Event, Level, Subscriber};
|
||||||
|
use tracing_subscriber::layer::Context;
|
||||||
|
use tracing_subscriber::Layer;
|
||||||
|
use zbus::connection::Connection;
|
||||||
|
|
||||||
|
use crate::Service;
|
||||||
|
|
||||||
|
#[zbus::proxy(
|
||||||
|
interface = "com.steampowered.SteamOSLogSubmitter.Manager",
|
||||||
|
default_service = "com.steampowered.SteamOSLogSubmitter",
|
||||||
|
default_path = "/com/steampowered/SteamOSLogSubmitter/Manager"
|
||||||
|
)]
|
||||||
|
trait Daemon {
|
||||||
|
async fn log(
|
||||||
|
&self,
|
||||||
|
timestamp: f64,
|
||||||
|
module: &str,
|
||||||
|
level: u32,
|
||||||
|
message: &str,
|
||||||
|
) -> zbus::Result<()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct StringVisitor {
|
||||||
|
string: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LogLine {
|
||||||
|
timestamp: f64,
|
||||||
|
module: String,
|
||||||
|
level: u32,
|
||||||
|
message: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LogReceiver
|
||||||
|
where
|
||||||
|
Self: 'static,
|
||||||
|
{
|
||||||
|
receiver: UnboundedReceiver<LogLine>,
|
||||||
|
sender: UnboundedSender<LogLine>,
|
||||||
|
proxy: DaemonProxy<'static>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LogLayer {
|
||||||
|
queue: UnboundedSender<LogLine>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Visit for StringVisitor {
|
||||||
|
fn record_debug(&mut self, _: &Field, value: &dyn Debug) {
|
||||||
|
self.string.push_str(format!("{value:?}").as_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LogReceiver {
|
||||||
|
pub async fn new(connection: Connection) -> Result<LogReceiver> {
|
||||||
|
let proxy = DaemonProxy::new(&connection).await?;
|
||||||
|
let (sender, receiver) = unbounded_channel();
|
||||||
|
Ok(LogReceiver {
|
||||||
|
receiver,
|
||||||
|
sender,
|
||||||
|
proxy,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Service for LogReceiver {
|
||||||
|
const NAME: &'static str = "SLS log receiver";
|
||||||
|
|
||||||
|
async fn run(&mut self) -> Result<()> {
|
||||||
|
while let Some(message) = self.receiver.recv().await {
|
||||||
|
let _ = self
|
||||||
|
.proxy
|
||||||
|
.log(
|
||||||
|
message.timestamp,
|
||||||
|
message.module.as_ref(),
|
||||||
|
message.level,
|
||||||
|
message.message.as_ref(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LogLayer {
|
||||||
|
pub async fn new(receiver: &LogReceiver) -> LogLayer {
|
||||||
|
LogLayer {
|
||||||
|
queue: receiver.sender.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: Subscriber + for<'a> tracing_subscriber::registry::LookupSpan<'a>> Layer<S> for LogLayer {
|
||||||
|
fn on_event(&self, event: &Event<'_>, _ctx: Context<'_, S>) {
|
||||||
|
let target = event.metadata().target();
|
||||||
|
if !target.starts_with("steamos_workerd::sls") {
|
||||||
|
// Don't forward non-SLS-related logs to SLS
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let target = target
|
||||||
|
.split("::")
|
||||||
|
.skip(2)
|
||||||
|
.fold(String::from("steamos_workerd"), |prefix, suffix| {
|
||||||
|
prefix + "." + suffix
|
||||||
|
});
|
||||||
|
let level = match *event.metadata().level() {
|
||||||
|
Level::TRACE => 10,
|
||||||
|
Level::DEBUG => 10,
|
||||||
|
Level::INFO => 20,
|
||||||
|
Level::WARN => 30,
|
||||||
|
Level::ERROR => 40,
|
||||||
|
};
|
||||||
|
let mut builder = StringVisitor {
|
||||||
|
string: String::new(),
|
||||||
|
};
|
||||||
|
event.record(&mut builder);
|
||||||
|
let text = builder.string;
|
||||||
|
let now = SystemTime::now();
|
||||||
|
let time = match now.duration_since(SystemTime::UNIX_EPOCH) {
|
||||||
|
Ok(duration) => duration.as_secs_f64(),
|
||||||
|
Err(_) => 0.0,
|
||||||
|
};
|
||||||
|
let _ = self.queue.send(LogLine {
|
||||||
|
timestamp: time,
|
||||||
|
module: target,
|
||||||
|
level,
|
||||||
|
message: text,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
47
src/testing.rs
Normal file
47
src/testing.rs
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use tempfile::{tempdir, TempDir};
|
||||||
|
|
||||||
|
thread_local! {
|
||||||
|
static TEST: RefCell<Option<Rc<Test>>> = RefCell::new(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start() -> TestHandle {
|
||||||
|
TEST.with(|lock| {
|
||||||
|
assert!(lock.borrow().as_ref().is_none());
|
||||||
|
let test: Rc<Test> = Rc::new(Test {
|
||||||
|
base: tempdir().expect("Couldn't create test directory"),
|
||||||
|
});
|
||||||
|
*lock.borrow_mut() = Some(test.clone());
|
||||||
|
TestHandle { test }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stop() {
|
||||||
|
TEST.with(|lock| *lock.borrow_mut() = None);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn current() -> Rc<Test> {
|
||||||
|
TEST.with(|lock| lock.borrow().as_ref().unwrap().clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Test {
|
||||||
|
base: TempDir,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TestHandle {
|
||||||
|
pub test: Rc<Test>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Test {
|
||||||
|
pub fn path(&self) -> &Path {
|
||||||
|
self.base.path()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for TestHandle {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue