├── .github
└── workflows
│ └── ci.yml
├── .gitignore
├── Cargo.lock
├── Cargo.toml
├── LICENSE
├── README.md
├── doc
├── ec-flow.svg
├── ec-flow.xml
└── ec-user-signals.txt
├── etc
├── dbus
│ └── org.surface.dtx.conf
├── dtx
│ ├── attach.sh
│ ├── detach.sh
│ ├── surface-dtx-daemon.conf
│ └── surface-dtx-userd.conf
├── systemd
│ ├── surface-dtx-daemon.service
│ └── surface-dtx-userd.service
└── udev
│ └── 40-surface_dtx.rules
├── pkg
├── bin
│ ├── .gitignore
│ └── makebin
├── deb
│ ├── .gitignore
│ ├── debian
│ │ ├── changelog
│ │ ├── compat
│ │ ├── control
│ │ └── rules
│ └── makedeb
└── fedora
│ ├── makerpm
│ └── surface-dtx-daemon.spec
├── surface-dtx-daemon
├── Cargo.toml
├── build.rs
└── src
│ ├── cli.rs
│ ├── config.rs
│ ├── logic
│ ├── core.rs
│ ├── mod.rs
│ ├── proc.rs
│ └── srvc.rs
│ ├── main.rs
│ ├── service
│ ├── arg.rs
│ ├── event.rs
│ ├── mod.rs
│ └── prop.rs
│ └── utils
│ ├── mod.rs
│ ├── scope.rs
│ ├── task.rs
│ ├── taskq.rs
│ └── tracing.rs
└── surface-dtx-userd
├── Cargo.toml
├── build.rs
└── src
├── cli.rs
├── config.rs
├── logic
├── core.rs
├── mod.rs
└── types.rs
├── main.rs
└── utils
├── mod.rs
├── notify.rs
└── task.rs
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | - feature/ci
8 |
9 | tags:
10 | - v[0-9]+.*
11 | - testing-ci.*
12 |
13 | pull_request:
14 |
15 | jobs:
16 | lint:
17 | name: Clippy
18 | runs-on: ubuntu-latest
19 | steps:
20 | - name: Checkout code
21 | uses: actions/checkout@v4
22 |
23 | - name: Install rust
24 | run: |
25 | rustup update stable && rustup default stable
26 | rustup component add clippy
27 |
28 | - name: Install dependencies
29 | run: sudo apt-get install libdbus-1-dev
30 |
31 | - name: Run clippy
32 | run: cargo clippy --all --all-features -- -Dwarnings
33 |
34 | test:
35 | name: Test
36 | runs-on: ubuntu-latest
37 |
38 | strategy:
39 | matrix:
40 | toolchain: [stable, nightly]
41 |
42 | steps:
43 | - name: Checkout code
44 | uses: actions/checkout@v4
45 |
46 | - name: Install rust
47 | run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }}
48 |
49 | - name: Install dependencies
50 | run: sudo apt-get install libdbus-1-dev
51 |
52 | - name: Build
53 | run: cargo build --all
54 |
55 | - name: Test
56 | run: cargo test --all
57 |
58 | build-bin:
59 | if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/')
60 |
61 | name: Build binary package
62 | runs-on: ubuntu-22.04
63 | needs: [lint, test]
64 |
65 | steps:
66 | - name: Checkout code
67 | uses: actions/checkout@v4
68 |
69 | - name: Install rust
70 | run: rustup update stable && rustup default stable
71 |
72 | - name: Install dependencies
73 | run: sudo apt-get install libdbus-1-dev
74 |
75 | - name: Build package
76 | run: ./pkg/bin/makebin
77 |
78 | - name: Prepare release
79 | run: mkdir release && mv pkg/bin/*.tar.xz release
80 |
81 | - name: Upload artifacts
82 | uses: actions/upload-artifact@v4
83 | with:
84 | name: binary-latest
85 | path: release
86 |
87 | build-deb:
88 | if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/')
89 |
90 | name: Build deb package
91 | runs-on: ubuntu-22.04
92 | needs: [lint, test]
93 |
94 | steps:
95 | - name: Checkout code
96 | uses: actions/checkout@v4
97 |
98 | - name: Install rust
99 | run: rustup update stable && rustup default stable
100 |
101 | - name: Install dependencies
102 | run: sudo apt-get install debhelper fakeroot dpkg-sig libdbus-1-dev
103 |
104 | - name: Build package
105 | run: ./pkg/deb/makedeb
106 |
107 | - name: Sign package
108 | env:
109 | GPG_KEY_ID: 56C464BAAC421453
110 | GPG_KEY: ${{ secrets.LINUX_SURFACE_GPG_KEY }}
111 | run: |
112 | # import GPG key
113 | echo "$GPG_KEY" | base64 -d | gpg --import --no-tty --batch --yes
114 | export GPG_TTY=$(tty)
115 | # sign package
116 | cd pkg/deb && dpkg-sig -g "--batch --no-tty" --sign builder -k $GPG_KEY_ID ./*.deb
117 | - name: Prepare release
118 | run: mkdir release && mv pkg/deb/*.deb release
119 |
120 | - name: Upload artifacts
121 | uses: actions/upload-artifact@v4
122 | with:
123 | name: debian-latest
124 | path: release
125 |
126 | build-f40:
127 | if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/')
128 |
129 | name: Build Fedora 40 package
130 | runs-on: ubuntu-latest
131 | needs: [lint, test]
132 | container:
133 | image: registry.fedoraproject.org/fedora:40
134 | options: --security-opt seccomp=unconfined
135 |
136 | steps:
137 | - name: Checkout code
138 | uses: actions/checkout@v4
139 |
140 | - name: Install build dependencies
141 | run: |
142 | dnf distro-sync -y
143 | dnf install -y rpmdevtools rpm-sign 'dnf-command(builddep)'
144 | dnf builddep -y pkg/fedora/surface-dtx-daemon.spec
145 |
146 | - name: Build package
147 | run: |
148 | cd pkg/fedora
149 | # Build the .rpm packages
150 | ./makerpm
151 |
152 | - name: Sign packages
153 | env:
154 | GPG_KEY_ID: 56C464BAAC421453
155 | GPG_KEY: ${{ secrets.LINUX_SURFACE_GPG_KEY }}
156 | run: |
157 | cd pkg/fedora/out/x86_64
158 |
159 | # import GPG key
160 | echo "$GPG_KEY" | base64 -d | gpg --import --no-tty --batch --yes
161 |
162 | # sign package
163 | rpm --resign *.rpm --define "_gpg_name $GPG_KEY_ID"
164 |
165 | - name: Upload artifacts
166 | uses: actions/upload-artifact@v4
167 | with:
168 | name: fedora-40-latest
169 | path: pkg/fedora/out/x86_64
170 |
171 | build-f41:
172 | if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/')
173 |
174 | name: Build Fedora 41 package
175 | runs-on: ubuntu-latest
176 | needs: [lint, test]
177 | container:
178 | image: registry.fedoraproject.org/fedora:41
179 | options: --security-opt seccomp=unconfined
180 |
181 | steps:
182 | - name: Checkout code
183 | uses: actions/checkout@v4
184 |
185 | - name: Install build dependencies
186 | run: |
187 | dnf distro-sync -y
188 | dnf install -y rpmdevtools rpm-sign 'dnf-command(builddep)'
189 | dnf builddep -y pkg/fedora/surface-dtx-daemon.spec
190 |
191 | - name: Build package
192 | run: |
193 | cd pkg/fedora
194 | # Build the .rpm packages
195 | ./makerpm
196 |
197 | - name: Sign packages
198 | env:
199 | GPG_KEY_ID: 56C464BAAC421453
200 | GPG_KEY: ${{ secrets.LINUX_SURFACE_GPG_KEY }}
201 | run: |
202 | cd pkg/fedora/out/x86_64
203 |
204 | # import GPG key
205 | echo "$GPG_KEY" | base64 -d | gpg --import --no-tty --batch --yes
206 |
207 | # sign package
208 | rpm --resign *.rpm --define "_gpg_name $GPG_KEY_ID"
209 |
210 | - name: Upload artifacts
211 | uses: actions/upload-artifact@v4
212 | with:
213 | name: fedora-41-latest
214 | path: pkg/fedora/out/x86_64
215 |
216 | build-f42:
217 | if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/')
218 |
219 | name: Build Fedora 42 package
220 | runs-on: ubuntu-latest
221 | needs: [lint, test]
222 | container:
223 | image: registry.fedoraproject.org/fedora:42
224 | options: --security-opt seccomp=unconfined
225 |
226 | steps:
227 | - name: Checkout code
228 | uses: actions/checkout@v4
229 |
230 | - name: Install build dependencies
231 | run: |
232 | dnf distro-sync -y
233 | dnf install -y rpmdevtools rpm-sign 'dnf-command(builddep)'
234 | dnf builddep -y pkg/fedora/surface-dtx-daemon.spec
235 |
236 | - name: Build package
237 | run: |
238 | cd pkg/fedora
239 | # Build the .rpm packages
240 | ./makerpm
241 |
242 | - name: Sign packages
243 | env:
244 | GPG_KEY_ID: 56C464BAAC421453
245 | GPG_KEY: ${{ secrets.LINUX_SURFACE_GPG_KEY }}
246 | run: |
247 | cd pkg/fedora/out/x86_64
248 |
249 | # import GPG key
250 | echo "$GPG_KEY" | base64 -d | gpg --import --no-tty --batch --yes
251 |
252 | # sign package
253 | rpm --resign *.rpm --define "_gpg_name $GPG_KEY_ID"
254 |
255 | - name: Upload artifacts
256 | uses: actions/upload-artifact@v4
257 | with:
258 | name: fedora-42-latest
259 | path: pkg/fedora/out/x86_64
260 |
261 | release:
262 | if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/')
263 |
264 | name: Publish release
265 | needs: [build-bin, build-deb, build-f40, build-f41, build-f42]
266 | runs-on: ubuntu-latest
267 |
268 | steps:
269 | - name: Download binary artifacts
270 | uses: actions/download-artifact@v4
271 | with:
272 | name: binary-latest
273 | path: binary-latest
274 |
275 | - name: Download Debian artifacts
276 | uses: actions/download-artifact@v4
277 | with:
278 | name: debian-latest
279 | path: debian-latest
280 |
281 | - name: Download Fedora 40 artifacts
282 | uses: actions/download-artifact@v4
283 | with:
284 | name: fedora-40-latest
285 | path: fedora-40-latest
286 |
287 | - name: Download Fedora 41 artifacts
288 | uses: actions/download-artifact@v4
289 | with:
290 | name: fedora-41-latest
291 | path: fedora-41-latest
292 |
293 | - name: Download Fedora 42 artifacts
294 | uses: actions/download-artifact@v4
295 | with:
296 | name: fedora-42-latest
297 | path: fedora-42-latest
298 |
299 | - name: Upload assets
300 | uses: svenstaro/upload-release-action@v2
301 | with:
302 | repo_token: ${{ secrets.GITHUB_TOKEN }}
303 | file: ./*-latest/*
304 | tag: ${{ github.ref }}
305 | overwrite: true
306 | file_glob: true
307 |
308 | repo-deb:
309 | name: Update Debian package repository
310 | needs: [release]
311 | runs-on: ubuntu-latest
312 | container: debian:sid
313 | steps:
314 | - name: Install dependencies
315 | run: |
316 | apt-get update
317 | apt-get install -y git
318 |
319 | - name: Download artifacts
320 | uses: actions/download-artifact@v4
321 | with:
322 | name: debian-latest
323 | path: debian-latest
324 |
325 | - name: Update repository
326 | env:
327 | SURFACEBOT_TOKEN: ${{ secrets.LINUX_SURFACE_BOT_TOKEN }}
328 | BRANCH_STAGING: u/staging
329 | GIT_REF: ${{ github.ref }}
330 | run: |
331 | repo="https://surfacebot:${SURFACEBOT_TOKEN}@github.com/linux-surface/repo.git"
332 |
333 | # clone package repository
334 | git clone -b "${BRANCH_STAGING}" "${repo}" repo
335 |
336 | # copy packages
337 | cp debian-latest/* repo/debian/
338 | cd repo/debian
339 |
340 | # parse git tag from ref
341 | GIT_TAG=$(echo $GIT_REF | sed 's|^refs/tags/||g')
342 |
343 | # convert packages into references
344 | for pkg in $(find . -name '*.deb'); do
345 | echo "surface-dtx-daemon:$GIT_TAG/$(basename $pkg)" > $pkg.blob
346 | rm $pkg
347 | done
348 |
349 | # set git identity
350 | git config --global user.email "surfacebot@users.noreply.github.com"
351 | git config --global user.name "surfacebot"
352 |
353 | # commit and push
354 | update_branch="${BRANCH_STAGING}-$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1)"
355 | git switch -c "${update_branch}"
356 | git add .
357 | git commit -m "Update Debian DTX daemon"
358 | git push --set-upstream origin "${update_branch}"
359 |
360 | repo-f40:
361 | name: Update Fedora 40 package repository
362 | needs: [release]
363 | runs-on: ubuntu-latest
364 | container:
365 | image: registry.fedoraproject.org/fedora:40
366 | options: --security-opt seccomp=unconfined
367 | steps:
368 | - name: Install dependencies
369 | run: |
370 | dnf install -y git findutils
371 |
372 | - name: Download artifacts
373 | uses: actions/download-artifact@v4
374 | with:
375 | name: fedora-40-latest
376 | path: fedora-40-latest
377 |
378 | - name: Update repository
379 | env:
380 | SURFACEBOT_TOKEN: ${{ secrets.LINUX_SURFACE_BOT_TOKEN }}
381 | BRANCH_STAGING: u/staging
382 | GIT_REF: ${{ github.ref }}
383 | run: |
384 | repo="https://surfacebot:${SURFACEBOT_TOKEN}@github.com/linux-surface/repo.git"
385 |
386 | # clone package repository
387 | git clone -b "${BRANCH_STAGING}" "${repo}" repo
388 |
389 | # copy packages
390 | cp fedora-40-latest/* repo/fedora/f40
391 | cd repo/fedora/f40
392 |
393 | # parse git tag from ref
394 | GIT_TAG=$(echo $GIT_REF | sed 's|^refs/tags/||g')
395 |
396 | # convert packages into references
397 | for pkg in $(find . -name '*.rpm'); do
398 | echo "surface-dtx-daemon:$GIT_TAG/$(basename $pkg)" > $pkg.blob
399 | rm $pkg
400 | done
401 |
402 | # set git identity
403 | git config --global user.email "surfacebot@users.noreply.github.com"
404 | git config --global user.name "surfacebot"
405 |
406 | # commit and push
407 | update_branch="${BRANCH_STAGING}-$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1)"
408 | git checkout -b "${update_branch}"
409 | git add .
410 | git commit -m "Update Fedora 40 DTX daemon"
411 | git push --set-upstream origin "${update_branch}"
412 |
413 | repo-f41:
414 | name: Update Fedora 41 package repository
415 | needs: [release]
416 | runs-on: ubuntu-latest
417 | container:
418 | image: registry.fedoraproject.org/fedora:41
419 | options: --security-opt seccomp=unconfined
420 | steps:
421 | - name: Install dependencies
422 | run: |
423 | dnf install -y git findutils
424 |
425 | - name: Download artifacts
426 | uses: actions/download-artifact@v4
427 | with:
428 | name: fedora-41-latest
429 | path: fedora-41-latest
430 |
431 | - name: Update repository
432 | env:
433 | SURFACEBOT_TOKEN: ${{ secrets.LINUX_SURFACE_BOT_TOKEN }}
434 | BRANCH_STAGING: u/staging
435 | GIT_REF: ${{ github.ref }}
436 | run: |
437 | repo="https://surfacebot:${SURFACEBOT_TOKEN}@github.com/linux-surface/repo.git"
438 |
439 | # clone package repository
440 | git clone -b "${BRANCH_STAGING}" "${repo}" repo
441 |
442 | # copy packages
443 | cp fedora-41-latest/* repo/fedora/f41
444 | cd repo/fedora/f41
445 |
446 | # parse git tag from ref
447 | GIT_TAG=$(echo $GIT_REF | sed 's|^refs/tags/||g')
448 |
449 | # convert packages into references
450 | for pkg in $(find . -name '*.rpm'); do
451 | echo "surface-dtx-daemon:$GIT_TAG/$(basename $pkg)" > $pkg.blob
452 | rm $pkg
453 | done
454 |
455 | # set git identity
456 | git config --global user.email "surfacebot@users.noreply.github.com"
457 | git config --global user.name "surfacebot"
458 |
459 | # commit and push
460 | update_branch="${BRANCH_STAGING}-$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1)"
461 | git checkout -b "${update_branch}"
462 | git add .
463 | git commit -m "Update Fedora 41 DTX daemon"
464 | git push --set-upstream origin "${update_branch}"
465 |
466 | repo-f42:
467 | name: Update Fedora 42 package repository
468 | needs: [release]
469 | runs-on: ubuntu-latest
470 | container:
471 | image: registry.fedoraproject.org/fedora:42
472 | options: --security-opt seccomp=unconfined
473 | steps:
474 | - name: Install dependencies
475 | run: |
476 | dnf install -y git findutils
477 |
478 | - name: Download artifacts
479 | uses: actions/download-artifact@v4
480 | with:
481 | name: fedora-42-latest
482 | path: fedora-42-latest
483 |
484 | - name: Update repository
485 | env:
486 | SURFACEBOT_TOKEN: ${{ secrets.LINUX_SURFACE_BOT_TOKEN }}
487 | BRANCH_STAGING: u/staging
488 | GIT_REF: ${{ github.ref }}
489 | run: |
490 | repo="https://surfacebot:${SURFACEBOT_TOKEN}@github.com/linux-surface/repo.git"
491 |
492 | # clone package repository
493 | git clone -b "${BRANCH_STAGING}" "${repo}" repo
494 |
495 | # copy packages
496 | cp fedora-42-latest/* repo/fedora/f42
497 | cd repo/fedora/f42
498 |
499 | # parse git tag from ref
500 | GIT_TAG=$(echo $GIT_REF | sed 's|^refs/tags/||g')
501 |
502 | # convert packages into references
503 | for pkg in $(find . -name '*.rpm'); do
504 | echo "surface-dtx-daemon:$GIT_TAG/$(basename $pkg)" > $pkg.blob
505 | rm $pkg
506 | done
507 |
508 | # set git identity
509 | git config --global user.email "surfacebot@users.noreply.github.com"
510 | git config --global user.name "surfacebot"
511 |
512 | # commit and push
513 | update_branch="${BRANCH_STAGING}-$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1)"
514 | git checkout -b "${update_branch}"
515 | git add .
516 | git commit -m "Update Fedora 42 DTX daemon"
517 | git push --set-upstream origin "${update_branch}"
518 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | **/*.rs.bk
3 |
--------------------------------------------------------------------------------
/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 4
4 |
5 | [[package]]
6 | name = "addr2line"
7 | version = "0.24.2"
8 | source = "registry+https://github.com/rust-lang/crates.io-index"
9 | checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
10 | dependencies = [
11 | "gimli",
12 | ]
13 |
14 | [[package]]
15 | name = "adler2"
16 | version = "2.0.0"
17 | source = "registry+https://github.com/rust-lang/crates.io-index"
18 | checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
19 |
20 | [[package]]
21 | name = "aho-corasick"
22 | version = "1.1.3"
23 | source = "registry+https://github.com/rust-lang/crates.io-index"
24 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
25 | dependencies = [
26 | "memchr",
27 | ]
28 |
29 | [[package]]
30 | name = "anstream"
31 | version = "0.6.18"
32 | source = "registry+https://github.com/rust-lang/crates.io-index"
33 | checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
34 | dependencies = [
35 | "anstyle",
36 | "anstyle-parse",
37 | "anstyle-query",
38 | "anstyle-wincon",
39 | "colorchoice",
40 | "is_terminal_polyfill",
41 | "utf8parse",
42 | ]
43 |
44 | [[package]]
45 | name = "anstyle"
46 | version = "1.0.10"
47 | source = "registry+https://github.com/rust-lang/crates.io-index"
48 | checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
49 |
50 | [[package]]
51 | name = "anstyle-parse"
52 | version = "0.2.6"
53 | source = "registry+https://github.com/rust-lang/crates.io-index"
54 | checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
55 | dependencies = [
56 | "utf8parse",
57 | ]
58 |
59 | [[package]]
60 | name = "anstyle-query"
61 | version = "1.1.2"
62 | source = "registry+https://github.com/rust-lang/crates.io-index"
63 | checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
64 | dependencies = [
65 | "windows-sys 0.59.0",
66 | ]
67 |
68 | [[package]]
69 | name = "anstyle-wincon"
70 | version = "3.0.7"
71 | source = "registry+https://github.com/rust-lang/crates.io-index"
72 | checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e"
73 | dependencies = [
74 | "anstyle",
75 | "once_cell",
76 | "windows-sys 0.59.0",
77 | ]
78 |
79 | [[package]]
80 | name = "anyhow"
81 | version = "1.0.98"
82 | source = "registry+https://github.com/rust-lang/crates.io-index"
83 | checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
84 |
85 | [[package]]
86 | name = "autocfg"
87 | version = "1.4.0"
88 | source = "registry+https://github.com/rust-lang/crates.io-index"
89 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
90 |
91 | [[package]]
92 | name = "backtrace"
93 | version = "0.3.74"
94 | source = "registry+https://github.com/rust-lang/crates.io-index"
95 | checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
96 | dependencies = [
97 | "addr2line",
98 | "cfg-if",
99 | "libc",
100 | "miniz_oxide",
101 | "object",
102 | "rustc-demangle",
103 | "windows-targets",
104 | ]
105 |
106 | [[package]]
107 | name = "bitflags"
108 | version = "2.9.0"
109 | source = "registry+https://github.com/rust-lang/crates.io-index"
110 | checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
111 |
112 | [[package]]
113 | name = "bytes"
114 | version = "1.10.1"
115 | source = "registry+https://github.com/rust-lang/crates.io-index"
116 | checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
117 |
118 | [[package]]
119 | name = "cfg-if"
120 | version = "1.0.0"
121 | source = "registry+https://github.com/rust-lang/crates.io-index"
122 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
123 |
124 | [[package]]
125 | name = "cfg_aliases"
126 | version = "0.2.1"
127 | source = "registry+https://github.com/rust-lang/crates.io-index"
128 | checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
129 |
130 | [[package]]
131 | name = "clap"
132 | version = "4.5.37"
133 | source = "registry+https://github.com/rust-lang/crates.io-index"
134 | checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071"
135 | dependencies = [
136 | "clap_builder",
137 | ]
138 |
139 | [[package]]
140 | name = "clap_builder"
141 | version = "4.5.37"
142 | source = "registry+https://github.com/rust-lang/crates.io-index"
143 | checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2"
144 | dependencies = [
145 | "anstream",
146 | "anstyle",
147 | "clap_lex",
148 | "strsim",
149 | ]
150 |
151 | [[package]]
152 | name = "clap_complete"
153 | version = "4.5.47"
154 | source = "registry+https://github.com/rust-lang/crates.io-index"
155 | checksum = "c06f5378ea264ad4f82bbc826628b5aad714a75abf6ece087e923010eb937fb6"
156 | dependencies = [
157 | "clap",
158 | ]
159 |
160 | [[package]]
161 | name = "clap_lex"
162 | version = "0.7.4"
163 | source = "registry+https://github.com/rust-lang/crates.io-index"
164 | checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
165 |
166 | [[package]]
167 | name = "colorchoice"
168 | version = "1.0.3"
169 | source = "registry+https://github.com/rust-lang/crates.io-index"
170 | checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
171 |
172 | [[package]]
173 | name = "dbus"
174 | version = "0.9.7"
175 | source = "registry+https://github.com/rust-lang/crates.io-index"
176 | checksum = "1bb21987b9fb1613058ba3843121dd18b163b254d8a6e797e144cbac14d96d1b"
177 | dependencies = [
178 | "futures-channel",
179 | "futures-util",
180 | "libc",
181 | "libdbus-sys",
182 | "winapi",
183 | ]
184 |
185 | [[package]]
186 | name = "dbus-crossroads"
187 | version = "0.5.2"
188 | source = "registry+https://github.com/rust-lang/crates.io-index"
189 | checksum = "3a4c83437187544ba5142427746835061b330446ca8902eabd70e4afb8f76de0"
190 | dependencies = [
191 | "dbus",
192 | ]
193 |
194 | [[package]]
195 | name = "dbus-tokio"
196 | version = "0.7.6"
197 | source = "registry+https://github.com/rust-lang/crates.io-index"
198 | checksum = "007688d459bc677131c063a3a77fb899526e17b7980f390b69644bdbc41fad13"
199 | dependencies = [
200 | "dbus",
201 | "libc",
202 | "tokio",
203 | ]
204 |
205 | [[package]]
206 | name = "equivalent"
207 | version = "1.0.2"
208 | source = "registry+https://github.com/rust-lang/crates.io-index"
209 | checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
210 |
211 | [[package]]
212 | name = "futures"
213 | version = "0.3.31"
214 | source = "registry+https://github.com/rust-lang/crates.io-index"
215 | checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
216 | dependencies = [
217 | "futures-channel",
218 | "futures-core",
219 | "futures-executor",
220 | "futures-io",
221 | "futures-sink",
222 | "futures-task",
223 | "futures-util",
224 | ]
225 |
226 | [[package]]
227 | name = "futures-channel"
228 | version = "0.3.31"
229 | source = "registry+https://github.com/rust-lang/crates.io-index"
230 | checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
231 | dependencies = [
232 | "futures-core",
233 | "futures-sink",
234 | ]
235 |
236 | [[package]]
237 | name = "futures-core"
238 | version = "0.3.31"
239 | source = "registry+https://github.com/rust-lang/crates.io-index"
240 | checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
241 |
242 | [[package]]
243 | name = "futures-executor"
244 | version = "0.3.31"
245 | source = "registry+https://github.com/rust-lang/crates.io-index"
246 | checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
247 | dependencies = [
248 | "futures-core",
249 | "futures-task",
250 | "futures-util",
251 | ]
252 |
253 | [[package]]
254 | name = "futures-io"
255 | version = "0.3.31"
256 | source = "registry+https://github.com/rust-lang/crates.io-index"
257 | checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
258 |
259 | [[package]]
260 | name = "futures-macro"
261 | version = "0.3.31"
262 | source = "registry+https://github.com/rust-lang/crates.io-index"
263 | checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
264 | dependencies = [
265 | "proc-macro2",
266 | "quote",
267 | "syn",
268 | ]
269 |
270 | [[package]]
271 | name = "futures-sink"
272 | version = "0.3.31"
273 | source = "registry+https://github.com/rust-lang/crates.io-index"
274 | checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
275 |
276 | [[package]]
277 | name = "futures-task"
278 | version = "0.3.31"
279 | source = "registry+https://github.com/rust-lang/crates.io-index"
280 | checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
281 |
282 | [[package]]
283 | name = "futures-util"
284 | version = "0.3.31"
285 | source = "registry+https://github.com/rust-lang/crates.io-index"
286 | checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
287 | dependencies = [
288 | "futures-channel",
289 | "futures-core",
290 | "futures-io",
291 | "futures-macro",
292 | "futures-sink",
293 | "futures-task",
294 | "memchr",
295 | "pin-project-lite",
296 | "pin-utils",
297 | "slab",
298 | ]
299 |
300 | [[package]]
301 | name = "gimli"
302 | version = "0.31.1"
303 | source = "registry+https://github.com/rust-lang/crates.io-index"
304 | checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
305 |
306 | [[package]]
307 | name = "hashbrown"
308 | version = "0.15.2"
309 | source = "registry+https://github.com/rust-lang/crates.io-index"
310 | checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
311 |
312 | [[package]]
313 | name = "indexmap"
314 | version = "2.9.0"
315 | source = "registry+https://github.com/rust-lang/crates.io-index"
316 | checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
317 | dependencies = [
318 | "equivalent",
319 | "hashbrown",
320 | ]
321 |
322 | [[package]]
323 | name = "is_terminal_polyfill"
324 | version = "1.70.1"
325 | source = "registry+https://github.com/rust-lang/crates.io-index"
326 | checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
327 |
328 | [[package]]
329 | name = "lazy_static"
330 | version = "1.5.0"
331 | source = "registry+https://github.com/rust-lang/crates.io-index"
332 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
333 |
334 | [[package]]
335 | name = "libc"
336 | version = "0.2.172"
337 | source = "registry+https://github.com/rust-lang/crates.io-index"
338 | checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
339 |
340 | [[package]]
341 | name = "libdbus-sys"
342 | version = "0.2.5"
343 | source = "registry+https://github.com/rust-lang/crates.io-index"
344 | checksum = "06085512b750d640299b79be4bad3d2fa90a9c00b1fd9e1b46364f66f0485c72"
345 | dependencies = [
346 | "pkg-config",
347 | ]
348 |
349 | [[package]]
350 | name = "log"
351 | version = "0.4.27"
352 | source = "registry+https://github.com/rust-lang/crates.io-index"
353 | checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
354 |
355 | [[package]]
356 | name = "matchers"
357 | version = "0.1.0"
358 | source = "registry+https://github.com/rust-lang/crates.io-index"
359 | checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
360 | dependencies = [
361 | "regex-automata 0.1.10",
362 | ]
363 |
364 | [[package]]
365 | name = "memchr"
366 | version = "2.7.4"
367 | source = "registry+https://github.com/rust-lang/crates.io-index"
368 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
369 |
370 | [[package]]
371 | name = "miniz_oxide"
372 | version = "0.8.8"
373 | source = "registry+https://github.com/rust-lang/crates.io-index"
374 | checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
375 | dependencies = [
376 | "adler2",
377 | ]
378 |
379 | [[package]]
380 | name = "mio"
381 | version = "1.0.3"
382 | source = "registry+https://github.com/rust-lang/crates.io-index"
383 | checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
384 | dependencies = [
385 | "libc",
386 | "wasi",
387 | "windows-sys 0.52.0",
388 | ]
389 |
390 | [[package]]
391 | name = "nix"
392 | version = "0.29.0"
393 | source = "registry+https://github.com/rust-lang/crates.io-index"
394 | checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
395 | dependencies = [
396 | "bitflags",
397 | "cfg-if",
398 | "cfg_aliases",
399 | "libc",
400 | ]
401 |
402 | [[package]]
403 | name = "nu-ansi-term"
404 | version = "0.46.0"
405 | source = "registry+https://github.com/rust-lang/crates.io-index"
406 | checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
407 | dependencies = [
408 | "overload",
409 | "winapi",
410 | ]
411 |
412 | [[package]]
413 | name = "object"
414 | version = "0.36.7"
415 | source = "registry+https://github.com/rust-lang/crates.io-index"
416 | checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
417 | dependencies = [
418 | "memchr",
419 | ]
420 |
421 | [[package]]
422 | name = "once_cell"
423 | version = "1.21.3"
424 | source = "registry+https://github.com/rust-lang/crates.io-index"
425 | checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
426 |
427 | [[package]]
428 | name = "overload"
429 | version = "0.1.1"
430 | source = "registry+https://github.com/rust-lang/crates.io-index"
431 | checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
432 |
433 | [[package]]
434 | name = "pin-project-lite"
435 | version = "0.2.16"
436 | source = "registry+https://github.com/rust-lang/crates.io-index"
437 | checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
438 |
439 | [[package]]
440 | name = "pin-utils"
441 | version = "0.1.0"
442 | source = "registry+https://github.com/rust-lang/crates.io-index"
443 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
444 |
445 | [[package]]
446 | name = "pkg-config"
447 | version = "0.3.32"
448 | source = "registry+https://github.com/rust-lang/crates.io-index"
449 | checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
450 |
451 | [[package]]
452 | name = "proc-macro2"
453 | version = "1.0.95"
454 | source = "registry+https://github.com/rust-lang/crates.io-index"
455 | checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
456 | dependencies = [
457 | "unicode-ident",
458 | ]
459 |
460 | [[package]]
461 | name = "quote"
462 | version = "1.0.40"
463 | source = "registry+https://github.com/rust-lang/crates.io-index"
464 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
465 | dependencies = [
466 | "proc-macro2",
467 | ]
468 |
469 | [[package]]
470 | name = "regex"
471 | version = "1.11.1"
472 | source = "registry+https://github.com/rust-lang/crates.io-index"
473 | checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
474 | dependencies = [
475 | "aho-corasick",
476 | "memchr",
477 | "regex-automata 0.4.9",
478 | "regex-syntax 0.8.5",
479 | ]
480 |
481 | [[package]]
482 | name = "regex-automata"
483 | version = "0.1.10"
484 | source = "registry+https://github.com/rust-lang/crates.io-index"
485 | checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
486 | dependencies = [
487 | "regex-syntax 0.6.29",
488 | ]
489 |
490 | [[package]]
491 | name = "regex-automata"
492 | version = "0.4.9"
493 | source = "registry+https://github.com/rust-lang/crates.io-index"
494 | checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
495 | dependencies = [
496 | "aho-corasick",
497 | "memchr",
498 | "regex-syntax 0.8.5",
499 | ]
500 |
501 | [[package]]
502 | name = "regex-syntax"
503 | version = "0.6.29"
504 | source = "registry+https://github.com/rust-lang/crates.io-index"
505 | checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
506 |
507 | [[package]]
508 | name = "regex-syntax"
509 | version = "0.8.5"
510 | source = "registry+https://github.com/rust-lang/crates.io-index"
511 | checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
512 |
513 | [[package]]
514 | name = "rustc-demangle"
515 | version = "0.1.24"
516 | source = "registry+https://github.com/rust-lang/crates.io-index"
517 | checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
518 |
519 | [[package]]
520 | name = "sdtx"
521 | version = "0.1.6"
522 | source = "git+https://github.com/linux-surface/libsurfacedtx?tag=v0.1.6#940c9a0cade870218ef8fee5f16df1eef8853c44"
523 | dependencies = [
524 | "futures",
525 | "nix",
526 | "smallvec",
527 | "thiserror",
528 | "tracing",
529 | ]
530 |
531 | [[package]]
532 | name = "sdtx-tokio"
533 | version = "0.1.6"
534 | source = "git+https://github.com/linux-surface/libsurfacedtx?tag=v0.1.6#940c9a0cade870218ef8fee5f16df1eef8853c44"
535 | dependencies = [
536 | "futures",
537 | "sdtx",
538 | "tokio",
539 | ]
540 |
541 | [[package]]
542 | name = "serde"
543 | version = "1.0.219"
544 | source = "registry+https://github.com/rust-lang/crates.io-index"
545 | checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
546 | dependencies = [
547 | "serde_derive",
548 | ]
549 |
550 | [[package]]
551 | name = "serde_derive"
552 | version = "1.0.219"
553 | source = "registry+https://github.com/rust-lang/crates.io-index"
554 | checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
555 | dependencies = [
556 | "proc-macro2",
557 | "quote",
558 | "syn",
559 | ]
560 |
561 | [[package]]
562 | name = "serde_ignored"
563 | version = "0.1.11"
564 | source = "registry+https://github.com/rust-lang/crates.io-index"
565 | checksum = "566da67d80e92e009728b3731ff0e5360cb181432b8ca73ea30bb1d170700d76"
566 | dependencies = [
567 | "serde",
568 | ]
569 |
570 | [[package]]
571 | name = "serde_spanned"
572 | version = "0.6.8"
573 | source = "registry+https://github.com/rust-lang/crates.io-index"
574 | checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
575 | dependencies = [
576 | "serde",
577 | ]
578 |
579 | [[package]]
580 | name = "sharded-slab"
581 | version = "0.1.7"
582 | source = "registry+https://github.com/rust-lang/crates.io-index"
583 | checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
584 | dependencies = [
585 | "lazy_static",
586 | ]
587 |
588 | [[package]]
589 | name = "signal-hook-registry"
590 | version = "1.4.4"
591 | source = "registry+https://github.com/rust-lang/crates.io-index"
592 | checksum = "a1ee1aca2bc74ef9589efa7ccaa0f3752751399940356209b3fd80c078149b5e"
593 | dependencies = [
594 | "libc",
595 | ]
596 |
597 | [[package]]
598 | name = "slab"
599 | version = "0.4.9"
600 | source = "registry+https://github.com/rust-lang/crates.io-index"
601 | checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
602 | dependencies = [
603 | "autocfg",
604 | ]
605 |
606 | [[package]]
607 | name = "smallvec"
608 | version = "1.15.0"
609 | source = "registry+https://github.com/rust-lang/crates.io-index"
610 | checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9"
611 |
612 | [[package]]
613 | name = "socket2"
614 | version = "0.5.9"
615 | source = "registry+https://github.com/rust-lang/crates.io-index"
616 | checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef"
617 | dependencies = [
618 | "libc",
619 | "windows-sys 0.52.0",
620 | ]
621 |
622 | [[package]]
623 | name = "strsim"
624 | version = "0.11.1"
625 | source = "registry+https://github.com/rust-lang/crates.io-index"
626 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
627 |
628 | [[package]]
629 | name = "surface-dtx-daemon"
630 | version = "0.3.9"
631 | dependencies = [
632 | "anyhow",
633 | "clap",
634 | "clap_complete",
635 | "dbus",
636 | "dbus-crossroads",
637 | "dbus-tokio",
638 | "futures",
639 | "libc",
640 | "nix",
641 | "sdtx",
642 | "sdtx-tokio",
643 | "serde",
644 | "serde_ignored",
645 | "tokio",
646 | "toml",
647 | "tracing",
648 | "tracing-subscriber",
649 | ]
650 |
651 | [[package]]
652 | name = "surface-dtx-userd"
653 | version = "0.3.9"
654 | dependencies = [
655 | "anyhow",
656 | "clap",
657 | "clap_complete",
658 | "dbus",
659 | "dbus-tokio",
660 | "futures",
661 | "serde",
662 | "serde_ignored",
663 | "tokio",
664 | "toml",
665 | "tracing",
666 | "tracing-subscriber",
667 | ]
668 |
669 | [[package]]
670 | name = "syn"
671 | version = "2.0.100"
672 | source = "registry+https://github.com/rust-lang/crates.io-index"
673 | checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
674 | dependencies = [
675 | "proc-macro2",
676 | "quote",
677 | "unicode-ident",
678 | ]
679 |
680 | [[package]]
681 | name = "thiserror"
682 | version = "2.0.12"
683 | source = "registry+https://github.com/rust-lang/crates.io-index"
684 | checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
685 | dependencies = [
686 | "thiserror-impl",
687 | ]
688 |
689 | [[package]]
690 | name = "thiserror-impl"
691 | version = "2.0.12"
692 | source = "registry+https://github.com/rust-lang/crates.io-index"
693 | checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
694 | dependencies = [
695 | "proc-macro2",
696 | "quote",
697 | "syn",
698 | ]
699 |
700 | [[package]]
701 | name = "thread_local"
702 | version = "1.1.8"
703 | source = "registry+https://github.com/rust-lang/crates.io-index"
704 | checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
705 | dependencies = [
706 | "cfg-if",
707 | "once_cell",
708 | ]
709 |
710 | [[package]]
711 | name = "tokio"
712 | version = "1.44.2"
713 | source = "registry+https://github.com/rust-lang/crates.io-index"
714 | checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48"
715 | dependencies = [
716 | "backtrace",
717 | "bytes",
718 | "libc",
719 | "mio",
720 | "pin-project-lite",
721 | "signal-hook-registry",
722 | "socket2",
723 | "tokio-macros",
724 | "windows-sys 0.52.0",
725 | ]
726 |
727 | [[package]]
728 | name = "tokio-macros"
729 | version = "2.5.0"
730 | source = "registry+https://github.com/rust-lang/crates.io-index"
731 | checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
732 | dependencies = [
733 | "proc-macro2",
734 | "quote",
735 | "syn",
736 | ]
737 |
738 | [[package]]
739 | name = "toml"
740 | version = "0.8.20"
741 | source = "registry+https://github.com/rust-lang/crates.io-index"
742 | checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148"
743 | dependencies = [
744 | "serde",
745 | "serde_spanned",
746 | "toml_datetime",
747 | "toml_edit",
748 | ]
749 |
750 | [[package]]
751 | name = "toml_datetime"
752 | version = "0.6.8"
753 | source = "registry+https://github.com/rust-lang/crates.io-index"
754 | checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
755 | dependencies = [
756 | "serde",
757 | ]
758 |
759 | [[package]]
760 | name = "toml_edit"
761 | version = "0.22.24"
762 | source = "registry+https://github.com/rust-lang/crates.io-index"
763 | checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474"
764 | dependencies = [
765 | "indexmap",
766 | "serde",
767 | "serde_spanned",
768 | "toml_datetime",
769 | "winnow",
770 | ]
771 |
772 | [[package]]
773 | name = "tracing"
774 | version = "0.1.41"
775 | source = "registry+https://github.com/rust-lang/crates.io-index"
776 | checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
777 | dependencies = [
778 | "pin-project-lite",
779 | "tracing-attributes",
780 | "tracing-core",
781 | ]
782 |
783 | [[package]]
784 | name = "tracing-attributes"
785 | version = "0.1.28"
786 | source = "registry+https://github.com/rust-lang/crates.io-index"
787 | checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
788 | dependencies = [
789 | "proc-macro2",
790 | "quote",
791 | "syn",
792 | ]
793 |
794 | [[package]]
795 | name = "tracing-core"
796 | version = "0.1.33"
797 | source = "registry+https://github.com/rust-lang/crates.io-index"
798 | checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
799 | dependencies = [
800 | "once_cell",
801 | "valuable",
802 | ]
803 |
804 | [[package]]
805 | name = "tracing-log"
806 | version = "0.2.0"
807 | source = "registry+https://github.com/rust-lang/crates.io-index"
808 | checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
809 | dependencies = [
810 | "log",
811 | "once_cell",
812 | "tracing-core",
813 | ]
814 |
815 | [[package]]
816 | name = "tracing-subscriber"
817 | version = "0.3.19"
818 | source = "registry+https://github.com/rust-lang/crates.io-index"
819 | checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
820 | dependencies = [
821 | "matchers",
822 | "nu-ansi-term",
823 | "once_cell",
824 | "regex",
825 | "sharded-slab",
826 | "smallvec",
827 | "thread_local",
828 | "tracing",
829 | "tracing-core",
830 | "tracing-log",
831 | ]
832 |
833 | [[package]]
834 | name = "unicode-ident"
835 | version = "1.0.18"
836 | source = "registry+https://github.com/rust-lang/crates.io-index"
837 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
838 |
839 | [[package]]
840 | name = "utf8parse"
841 | version = "0.2.2"
842 | source = "registry+https://github.com/rust-lang/crates.io-index"
843 | checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
844 |
845 | [[package]]
846 | name = "valuable"
847 | version = "0.1.1"
848 | source = "registry+https://github.com/rust-lang/crates.io-index"
849 | checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
850 |
851 | [[package]]
852 | name = "wasi"
853 | version = "0.11.0+wasi-snapshot-preview1"
854 | source = "registry+https://github.com/rust-lang/crates.io-index"
855 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
856 |
857 | [[package]]
858 | name = "winapi"
859 | version = "0.3.9"
860 | source = "registry+https://github.com/rust-lang/crates.io-index"
861 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
862 | dependencies = [
863 | "winapi-i686-pc-windows-gnu",
864 | "winapi-x86_64-pc-windows-gnu",
865 | ]
866 |
867 | [[package]]
868 | name = "winapi-i686-pc-windows-gnu"
869 | version = "0.4.0"
870 | source = "registry+https://github.com/rust-lang/crates.io-index"
871 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
872 |
873 | [[package]]
874 | name = "winapi-x86_64-pc-windows-gnu"
875 | version = "0.4.0"
876 | source = "registry+https://github.com/rust-lang/crates.io-index"
877 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
878 |
879 | [[package]]
880 | name = "windows-sys"
881 | version = "0.52.0"
882 | source = "registry+https://github.com/rust-lang/crates.io-index"
883 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
884 | dependencies = [
885 | "windows-targets",
886 | ]
887 |
888 | [[package]]
889 | name = "windows-sys"
890 | version = "0.59.0"
891 | source = "registry+https://github.com/rust-lang/crates.io-index"
892 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
893 | dependencies = [
894 | "windows-targets",
895 | ]
896 |
897 | [[package]]
898 | name = "windows-targets"
899 | version = "0.52.6"
900 | source = "registry+https://github.com/rust-lang/crates.io-index"
901 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
902 | dependencies = [
903 | "windows_aarch64_gnullvm",
904 | "windows_aarch64_msvc",
905 | "windows_i686_gnu",
906 | "windows_i686_gnullvm",
907 | "windows_i686_msvc",
908 | "windows_x86_64_gnu",
909 | "windows_x86_64_gnullvm",
910 | "windows_x86_64_msvc",
911 | ]
912 |
913 | [[package]]
914 | name = "windows_aarch64_gnullvm"
915 | version = "0.52.6"
916 | source = "registry+https://github.com/rust-lang/crates.io-index"
917 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
918 |
919 | [[package]]
920 | name = "windows_aarch64_msvc"
921 | version = "0.52.6"
922 | source = "registry+https://github.com/rust-lang/crates.io-index"
923 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
924 |
925 | [[package]]
926 | name = "windows_i686_gnu"
927 | version = "0.52.6"
928 | source = "registry+https://github.com/rust-lang/crates.io-index"
929 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
930 |
931 | [[package]]
932 | name = "windows_i686_gnullvm"
933 | version = "0.52.6"
934 | source = "registry+https://github.com/rust-lang/crates.io-index"
935 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
936 |
937 | [[package]]
938 | name = "windows_i686_msvc"
939 | version = "0.52.6"
940 | source = "registry+https://github.com/rust-lang/crates.io-index"
941 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
942 |
943 | [[package]]
944 | name = "windows_x86_64_gnu"
945 | version = "0.52.6"
946 | source = "registry+https://github.com/rust-lang/crates.io-index"
947 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
948 |
949 | [[package]]
950 | name = "windows_x86_64_gnullvm"
951 | version = "0.52.6"
952 | source = "registry+https://github.com/rust-lang/crates.io-index"
953 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
954 |
955 | [[package]]
956 | name = "windows_x86_64_msvc"
957 | version = "0.52.6"
958 | source = "registry+https://github.com/rust-lang/crates.io-index"
959 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
960 |
961 | [[package]]
962 | name = "winnow"
963 | version = "0.7.6"
964 | source = "registry+https://github.com/rust-lang/crates.io-index"
965 | checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10"
966 | dependencies = [
967 | "memchr",
968 | ]
969 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [workspace]
2 |
3 | members = [
4 | "surface-dtx-daemon",
5 | "surface-dtx-userd",
6 | ]
7 |
8 | [profile.release]
9 | lto = true
10 | codegen-units = 1
11 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Maximilian Luz
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Linux DTX Daemon for Surface Books
2 |
3 | 
4 |
5 | Linux User-Space Detachment System (DTX) Daemon for the Surface ACPI Driver (and Surface Books).
6 | Currently, only the Surface Book 2 and 3 are supported, due to lack of driver-support on the Surface Book 1.
7 | This may change in the future.
8 |
9 | ## About this Package
10 |
11 | This package contains two daemons.
12 | A system daemon (`surface-dtx-daemon`) and a per-user daemon (`surface-dtx-userd`):
13 |
14 | - The system daemon allows proper clipboard detachment on the Surface Book 2 and 3. It allows you to run commands before the clipboard is unlocked, after it has been re-attached, or when the unlocking-process has been aborted (e.g. by pressing the detach-button a second time).
15 | See the configuration section below for details.
16 | Furthermore, this daemon provides a d-bus interface via which you can query the current device mode (i.e. if the device is in tablet-, laptop- or studio-mode).
17 |
18 | - The per-user daemon is responsible for desktop-notifications, i.e. it notifies you when the clipboard can be physically detached (i.e. the latch holding it in place is unlocked), and when the re-attachment process has been completed, i.e. indicating when it is fully usable again after re-attachment.
19 | Running this daemon is completely optional, i.e. if you don't want any notifications, you are free to simply not run it.
20 |
21 | The split into two daemons is required as notifications can only be sent on a per-user basis.
22 |
23 | ## Installation
24 |
25 | If you have a Debian (Ubuntu, ...) based distribution, have a look at the [releases page][releases] for official packages.
26 | Official Arch Linux packages can be found in the AUR (`surface-dtx-daemon`).
27 | After installation, you may want to:
28 | - enable the systemd service for the system daemon using `systemctl enable surface-dtx-daemon.service`.
29 | - enable the systemd service for the per-user daemon using `systemctl enable --user surface-dtx-userd.service`.
30 |
31 | Alternatively, you can build these packages yourself, using the provided `PKGBUILD` (Arch Linux) or `makedeb.sh` script in the respective `pkg` subdirectories.
32 |
33 | ## Configuration
34 |
35 | The main configuration files can be found under
36 |
37 | - `/etc/surface-dtx/surface-dtx-daemon.conf` for the system daemon configuration, and
38 | - `/etc/surface-dtx/surface-dtx-userd.conf` for the per-user daemon configuration.
39 |
40 | Here you can specify the handler-scripts for supported events and other options.
41 | All options are explained in these files, the configuration language is TOML, default attach and detach handler scripts are included.
42 |
43 | Furthermore, a per-user configuration for the user daemon can also be created under `$XDG_CONFIG_HOME/surface-dtx/surface-dtx-userd.conf` (if not set, `$XDG_CONFIG_HOME` defaults to `.config`).
44 |
45 | ## Building a Package from Source
46 |
47 | ### Arch Linux
48 |
49 | Simply install `surface-dtx-daemon` from AUR or have a look at its PKGBUILD.
50 |
51 | ### Debian/Ubuntu
52 |
53 | Use the `makedeb` script provided under `pkg/deb`, i.e. run
54 | ```
55 | ./pkg/deb/makedeb
56 | ```
57 | from the root project directory.
58 | You may need to install the `build-essential` and `devscripts` packages beforehand.
59 | The final package will be in the `pkg/deb` directory.
60 |
61 |
62 | [releases]: https://github.com/linux-surface/surface-dtx-daemon/releases
63 |
--------------------------------------------------------------------------------
/doc/ec-flow.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
--------------------------------------------------------------------------------
/doc/ec-user-signals.txt:
--------------------------------------------------------------------------------
1 | CURRENT STATE SIGNAL NEXT STATE EVENTS/EFFECT
2 |
3 | connected-closed request connected-closed-waiting request event
4 | connected-closed cancel connected-closed no event
5 | connected-closed confirm connected-closed no event
6 | connected-closed heartbeat connected-closed no event
7 |
8 | connected-closed-waiting request connected-closed request event
9 | connected-closed-waiting cancel connected-closed request event
10 | connected-closed-waiting confirm connected-opened opened event
11 | connected-closed-waiting heartbeat connected-closed-waiting no event, timeout is reset
12 |
13 | connected-opened request connected-opened-pending request event
14 | connected-opened cancel connected-opened-pending request event
15 | connected-opened confirm connected-opened no event
16 | connected-opened heartbeat connected-opened no event
17 |
18 | connected-opened-pending request connected-opened-pending no event
19 | connected-opened-pending cancel connected-opened-pending no event
20 | connected-opened-pending confirm connected-opened-pending no event
21 | connected-opened-pending heartbeat connected-opened-pending no event
22 |
23 |
24 | disconnected-closed request disconnected-closed-waiting request event
25 | disconnected-closed cancel disconnected-closed no event
26 | disconnected-closed confirm disconnected-closed no event
27 | disconnected-closed heartbeat disconnected-closed no event
28 |
29 | disconnected-closed-waiting request disconnected-closed request event
30 | disconnected-closed-waiting cancel disconnected-closed request event
31 | disconnected-closed-waiting confirm disconnected-opened opened event
32 | disconnected-closed-waiting heartbeat disconnected-closed-waiting no event, timeout is reset
33 |
34 | disconnected-opened request disconnected-opened-pending request event
35 | disconnected-opened cancel disconnected-opened-pending request event
36 | disconnected-opened confirm disconnected-opened no event
37 | disconnected-opened heartbeat disconnected-opened no event
38 |
39 | disconnected-opened-pending request disconnected-opened-pending no event
40 | disconnected-opened-pending cancel disconnected-opened-pending no event
41 | disconnected-opened-pending confirm disconnected-opened-pending no event
42 | disconnected-opened-pending heartbeat disconnected-opened-pending no event
43 |
--------------------------------------------------------------------------------
/etc/dbus/org.surface.dtx.conf:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 | system
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/etc/dtx/attach.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | # surface-dtx detachment handler
3 |
--------------------------------------------------------------------------------
/etc/dtx/detach.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | # surface-dtx detachment handler
3 |
4 | # unmount all USB devices
5 | for usb_dev in /dev/disk/by-id/usb-*
6 | do
7 | dev=$(readlink -f $usb_dev)
8 | mount -l | grep -q "^$dev\s" && umount "$dev"
9 | done
10 |
11 | # signal commence
12 | exit $EXIT_DETACH_COMMENCE
13 | # The exit signal determines the continuation of the detachment-procedure. A
14 | # value of EXIT_DETACH_COMMENCE (0/success), causes the detachment procedure
15 | # to open the latch, while a value of EXIT_DETACH_ABORT (1, or any other
16 | # non-zero value) will cause the detachment-procedure to be aborted. On an
17 | # abort caused by this script, the detach_abort handler will _not_ be
18 | # executed. It is therefore the the responsibility of this handler-executable
19 | # to ensure the device state is properly reset to the state before its
20 | # execution, if required.
21 |
--------------------------------------------------------------------------------
/etc/dtx/surface-dtx-daemon.conf:
--------------------------------------------------------------------------------
1 | # Surface DTX System Daemon Configuration
2 |
3 |
4 | [log]
5 | # Log format options.
6 |
7 | level = "info"
8 | # The level used for logging.
9 | # Valid options are trace, debug, info, warning, error, and critical.
10 |
11 |
12 | [handler]
13 | # Event handler scripts.
14 | # All paths are relative to this file.
15 |
16 | [handler.detach]
17 | exec = "./detach.sh"
18 | # The executable to be executed before unlocking the clipboard.
19 | # If unspecified, no handler will be executed.
20 |
21 | #timeout =
22 | # Timeout for the executable, after which it will be killed.
23 | # Defaults to 60 seconds.
24 |
25 | [handler.detach_abort]
26 | exec = "./attach.sh"
27 | # The executable to be executed after the detach-process has been aborted.
28 | # This script will be executed only after completion of the detach script.
29 | # If unspecified, no handler will be executed.
30 |
31 | #timeout =
32 | # Timeout for the executable, after which it will be killed.
33 | # Defaults to 60 seconds.
34 |
35 | [handler.attach]
36 | exec = "./attach.sh"
37 | # The executable to be executed after the clipboard has been attached.
38 | # Before execution, the delay specified in delay.attach will be waited to
39 | # allow for all devices to be set up correctly.
40 | # If unspecified, no handler will be executed.
41 |
42 | #timeout =
43 | # Timeout for the executable, after which it will be killed.
44 | # Defaults to 60 seconds.
45 |
46 | #delay =
47 | # The delay in seconds to wait before executing the attach handler.
48 | # Defaults to 5 (seconds).
49 |
--------------------------------------------------------------------------------
/etc/dtx/surface-dtx-userd.conf:
--------------------------------------------------------------------------------
1 | # Surface DTX User Daemon Configuration
2 | # Use $XDG_CONFIG_HOME/surface-dtx/surface-dtx-userd.conf for per-user configuration.
3 |
4 |
5 | [log]
6 | # Log format options.
7 |
8 | level = "info"
9 | # The level used for logging.
10 | # Valid options are trace, debug, info, warning, error, and critical.
11 |
--------------------------------------------------------------------------------
/etc/systemd/surface-dtx-daemon.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=Surface Detachment System (DTX) Daemon
3 | Documentation=https://github.com/linux-surface/surface-dtx-daemon
4 | After=dev-surface-dtx.device
5 | Wants=dev-surface-dtx.device
6 |
7 | [Service]
8 | Type=simple
9 | ExecStart=/usr/bin/surface-dtx-daemon --no-log-time
10 |
11 | [Install]
12 | WantedBy=multi-user.target
13 |
--------------------------------------------------------------------------------
/etc/systemd/surface-dtx-userd.service:
--------------------------------------------------------------------------------
1 | [Unit]
2 | Description=Surface Detachment System (DTX) User Daemon
3 | Documentation=https://github.com/linux-surface/surface-dtx-daemon
4 | After=basic.target
5 | ConditionUser=!root
6 |
7 | [Service]
8 | Type=simple
9 | ExecStart=/usr/bin/surface-dtx-userd --no-log-time
10 |
11 | [Install]
12 | WantedBy=default.target
13 |
--------------------------------------------------------------------------------
/etc/udev/40-surface_dtx.rules:
--------------------------------------------------------------------------------
1 | KERNEL=="surface_dtx", TAG+="systemd"
2 |
--------------------------------------------------------------------------------
/pkg/bin/.gitignore:
--------------------------------------------------------------------------------
1 | pkg/
2 | src/
3 | *.tar
4 | *.tar.xz
5 |
--------------------------------------------------------------------------------
/pkg/bin/makebin:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 |
4 | pkgname="surface-dtx-daemon"
5 | pkgarch="x86_64"
6 |
7 | gitver=$(git describe --tags 2> /dev/null | sed 's/^v//;s/\([^-]*-g\)/r\1/;s/-/./g' || true)
8 | civer=$(echo $TRAVIS_TAG | sed 's/^v//;s/\([^-]*-g\)/r\1/;s/-/./g')
9 |
10 | pkgver=${civer:-${gitver:-0.0.0}}
11 | binpkg="$pkgname-$pkgver-$pkgarch.bin.tar.xz"
12 |
13 | branch="HEAD"
14 | basepath="$PWD/pkg/bin"
15 | srcdir="$basepath/src"
16 | pkgdir="$basepath/pkg"
17 |
18 |
19 | pushd() {
20 | command pushd "$@" > /dev/null
21 | }
22 |
23 | popd() {
24 | command popd "$@" > /dev/null
25 | }
26 |
27 |
28 | chkdir() {
29 | if [ ! -d ".git" ]
30 | then
31 | echo "Error: Script must be run from the root directory"
32 | exit 1
33 | fi
34 | }
35 |
36 | prepare() {
37 | archive="$basepath/src.tar"
38 |
39 | git archive --format tar "$branch" > "$archive"
40 |
41 | mkdir -p "$srcdir"
42 | tar xf "$archive" --directory "$srcdir"
43 | }
44 |
45 | build() {
46 | pushd "$srcdir"
47 | env CARGO_TARGET_DIR="$PWD/target" CARGO_INCREMENTAL=0 cargo build --release --locked
48 | strip --strip-all "target/release/surface-dtx-daemon"
49 | strip --strip-all "target/release/surface-dtx-userd"
50 | popd
51 | }
52 |
53 | package() {
54 | pushd "$srcdir"
55 |
56 | # clean package directory
57 | rm -rf "$pkgdir"
58 | mkdir -p "$pkgdir"
59 |
60 | # binary files
61 | install -D -m755 "target/release/surface-dtx-daemon" "$pkgdir/bin/surface-dtx-daemon"
62 | install -D -m755 "target/release/surface-dtx-userd" "$pkgdir/bin/surface-dtx-userd"
63 |
64 | # application files
65 | install -D -m644 "etc/dtx/surface-dtx-daemon.conf" "$pkgdir/surface-dtx/surface-dtx-daemon.conf"
66 | install -D -m644 "etc/dtx/surface-dtx-userd.conf" "$pkgdir/surface-dtx/surface-dtx-userd.conf"
67 | install -D -m755 "etc/dtx/attach.sh" "$pkgdir/surface-dtx/attach.sh"
68 | install -D -m755 "etc/dtx/detach.sh" "$pkgdir/surface-dtx/detach.sh"
69 |
70 | # systemd service files
71 | install -D -m644 "etc/systemd/surface-dtx-daemon.service" "$pkgdir/systemd/surface-dtx-daemon.service"
72 | install -D -m644 "etc/systemd/surface-dtx-userd.service" "$pkgdir/systemd/surface-dtx-userd.service"
73 |
74 | # dbus config file
75 | install -D -m644 "etc/dbus/org.surface.dtx.conf" "$pkgdir/dbus/org.surface.dtx.conf"
76 |
77 | # udev rules
78 | install -D -m644 "etc/udev/40-surface_dtx.rules" "$pkgdir/udev/40-surface_dtx.rules"
79 |
80 | # completion files
81 | install -D -m644 "target/surface-dtx-daemon.bash" "$pkgdir/shell-completions/surface-dtx-daemon.bash"
82 | install -D -m644 "target/surface-dtx-userd.bash" "$pkgdir/shell-completions/surface-dtx-userd.bash"
83 | install -D -m644 "target/_surface-dtx-daemon" "$pkgdir/shell-completions/surface-dtx-daemon.zsh"
84 | install -D -m644 "target/_surface-dtx-userd" "$pkgdir/shell-completions/surface-dtx-userd.zsh"
85 | install -D -m644 "target/surface-dtx-daemon.fish" "$pkgdir/shell-completions/surface-dtx-daemon.fish"
86 | install -D -m644 "target/surface-dtx-userd.fish" "$pkgdir/shell-completions/surface-dtx-userd.fish"
87 |
88 | # license
89 | install -D -m644 "LICENSE" "$pkgdir/LICENSE"
90 |
91 | # zip package
92 | tar -C "$pkgdir" -cJf "$basepath/$binpkg" .
93 |
94 | popd
95 | }
96 |
97 |
98 | chkdir
99 | prepare
100 | build
101 | package
102 |
--------------------------------------------------------------------------------
/pkg/deb/.gitignore:
--------------------------------------------------------------------------------
1 | src/
2 | *.tar
3 | *.buildinfo
4 | *.changes
5 | *.deb
--------------------------------------------------------------------------------
/pkg/deb/debian/changelog:
--------------------------------------------------------------------------------
1 | surface-dtx-daemon (0.3.9-1) unstable; urgency=medium
2 |
3 | * Update dependencies
4 |
5 | -- Maximilian Luz Sat, 19 Arp 2025 20:42:03 +0200
6 |
7 | surface-dtx-daemon (0.3.8-1) unstable; urgency=medium
8 |
9 | * Update dependencies
10 |
11 | -- Maximilian Luz Sat, 14 Sep 2024 16:00:43 +0200
12 |
13 | surface-dtx-daemon (0.3.7-1) unstable; urgency=medium
14 |
15 | * Update dependencies
16 |
17 | -- Maximilian Luz Thu, 14 Mar 2024 23:34:21 +0200
18 |
19 | surface-dtx-daemon (0.3.6-2) unstable; urgency=medium
20 |
21 | * Bump release for Fedora 39 build
22 |
23 | -- Maximilian Luz Tue, 03 Oct 2023 16:40:38 +0200
24 |
25 | surface-dtx-daemon (0.3.6-1) unstable; urgency=medium
26 |
27 | * Update dependencies
28 |
29 | -- Maximilian Luz Tue, 03 Oct 2023 16:04:19 +0200
30 |
31 | surface-dtx-daemon (0.3.5-1) unstable; urgency=medium
32 |
33 | * Update dependencies
34 | * Remove dependency on unmaintained atty crate
35 |
36 | -- Maximilian Luz Tue, 11 Jul 2023 22:02:40 +0200
37 |
38 | surface-dtx-daemon (0.3.4-1) unstable; urgency=medium
39 |
40 | * Update dependencies
41 |
42 | -- Maximilian Luz Wed, 19 Apr 2023 16:09:13 +0200
43 |
44 | surface-dtx-daemon (0.3.3-2) unstable; urgency=medium
45 |
46 | * Bump release to build for Fedora 37
47 |
48 | -- Maximilian Luz Fri, 14 Oct 2022 21:12:25 +0200
49 |
50 | surface-dtx-daemon (0.3.3-1) unstable; urgency=medium
51 |
52 | * Update dependencies
53 |
54 | -- Maximilian Luz Sat, 08 Oct 2022 12:40:08 +0200
55 |
56 | surface-dtx-daemon (0.3.2-1) unstable; urgency=medium
57 |
58 | * Update dependencies
59 |
60 | -- Maximilian Luz Thu, 28 Apr 2022 02:00:32 +0200
61 |
62 | surface-dtx-daemon (0.3.1-3) unstable; urgency=medium
63 |
64 | * Bump release to build for Fedora 36
65 |
66 | -- Dorian Stoll Wed, 27 Apr 2022 20:05:00 +0200
67 |
68 | surface-dtx-daemon (0.3.1-2) unstable; urgency=medium
69 |
70 | * Bump release to build for Fedora 35
71 |
72 | -- Dorian Stoll Wed, 03 Nov 2021 19:48:00 +0100
73 |
74 | surface-dtx-daemon (0.3.1-1) unstable; urgency=medium
75 |
76 | * Fix typo causing the user-daemon to crash on latch error
77 | * Update dependencies
78 |
79 | -- Maximilian Luz Mon, 23 Aug 2021 02:09:22 +0200
80 |
81 | surface-dtx-daemon (0.3.0-1) unstable; urgency=medium
82 |
83 | * Properly forward hardware-errors, runtime-errors, and other notifications to user
84 | * Support DTX heartbeat commend
85 | * Support handler script timeouts
86 | * Various stability improvements
87 |
88 | -- Maximilian Luz Wed, 07 Apr 2021 03:48:02 +0200
89 |
90 | surface-dtx-daemon (0.2.0-2) unstable; urgency=medium
91 |
92 | * Bump release to build for Fedora 34
93 |
94 | -- Dorian Stoll Fri, 19 Mar 2021 08:06:24 +0100
95 |
96 | surface-dtx-daemon (0.2.0-1) unstable; urgency=medium
97 |
98 | * Update to new DTX kernel interface.
99 | * Update dependencies.
100 |
101 | -- Maximilian Luz Fri, 16 Oct 2020 20:09:24 +0200
102 |
103 | surface-dtx-daemon (0.1.5-2) unstable; urgency=medium
104 |
105 | * Bump release to build for Fedora 33
106 |
107 | -- Dorian Stoll Tue, 29 Sep 2020 18:47:56 +0200
108 |
109 | surface-dtx-daemon (0.1.5-1) unstable; urgency=medium
110 |
111 | * Update dependencies
112 |
113 | -- Maximilian Luz Sat, 04 Jul 2020 03:32:56 +0200
114 |
115 | surface-dtx-daemon (0.1.4-3) unstable; urgency=medium
116 |
117 | * Bump pkgrel
118 |
119 | -- Dorian Stoll Tue, 31 Mar 2020 13:56:21 +0200
120 |
121 | surface-dtx-daemon (0.1.4-2) unstable; urgency=medium
122 |
123 | * Bump pkgrel
124 |
125 | -- Maximilian Luz Tue, 18 Feb 2020 20:36:25 +0100
126 |
127 | surface-dtx-daemon (0.1.4) unstable; urgency=medium
128 |
129 | * Improve packaging strategy
130 | * Update third-party dependencies
131 |
132 | -- Maximilian Luz Mon, 09 Sep 2019 17:56:32 +0200
133 |
134 | surface-dtx-daemon (0.1.3) unstable; urgency=medium
135 |
136 | * Update third-party dependencies.
137 |
138 | -- Maximilian Luz Thu, 05 Sep 2019 02:45:46 +0200
139 |
140 | surface-dtx-daemon (0.1.2) unstable; urgency=medium
141 |
142 | * Add udev rule to ensure device is available.
143 |
144 | -- Maximilian Luz Thu, 09 May 2019 22:54:39 +0000
145 |
146 | surface-dtx-daemon (0.1.1) unstable; urgency=medium
147 |
148 | * Add request function to dbus interface.
149 | * Fix libc/libgcc dependencies.
150 |
151 | -- Maximilian Luz Wed, 08 May 2019 19:34:02 +0000
152 |
153 | surface-dtx-daemon (0.1.0) unstable; urgency=medium
154 |
155 | * Initial release.
156 |
157 | -- Maximilian Luz Sat, 20 Apr 2019 16:50:43 +0000
158 |
--------------------------------------------------------------------------------
/pkg/deb/debian/compat:
--------------------------------------------------------------------------------
1 | 10
2 |
--------------------------------------------------------------------------------
/pkg/deb/debian/control:
--------------------------------------------------------------------------------
1 | Source: surface-dtx-daemon
2 | Section: misc
3 | Priority: optional
4 | Maintainer: Maximilian Luz
5 | Build-Depends: build-essential, debhelper (>= 10), cargo, rustc (>= 1.34.0), libdbus-glib-1-dev
6 |
7 | Package: surface-dtx-daemon
8 | Architecture: amd64
9 | Depends: libc6 (>= 2.19), libgcc1 (>= 1:4.9.2), libdbus-1-3
10 | Description: Surface Detachment System (DTX) Daemon
11 |
--------------------------------------------------------------------------------
/pkg/deb/debian/rules:
--------------------------------------------------------------------------------
1 | #!/usr/bin/make -f
2 | export DH_VERBOSE = 1
3 |
4 | pkgdir = debian/surface-dtx-daemon
5 |
6 | clean:
7 | dh clean
8 | cargo clean
9 |
10 | build:
11 | dh build
12 | env CARGO_TARGET_DIR="${PWD}/target" CARGO_INCREMENTAL=0 cargo build --release --locked
13 |
14 | override_dh_install:
15 | # binary files
16 | install -D -m755 "target/release/surface-dtx-daemon" "${pkgdir}/usr/bin/surface-dtx-daemon"
17 | install -D -m755 "target/release/surface-dtx-userd" "${pkgdir}/usr/bin/surface-dtx-userd"
18 |
19 | # application files
20 | install -D -m644 "etc/dtx/surface-dtx-daemon.conf" "${pkgdir}/etc/surface-dtx/surface-dtx-daemon.conf"
21 | install -D -m644 "etc/dtx/surface-dtx-userd.conf" "${pkgdir}/etc/surface-dtx/surface-dtx-userd.conf"
22 | install -D -m755 "etc/dtx/attach.sh" "${pkgdir}/etc/surface-dtx/attach.sh"
23 | install -D -m755 "etc/dtx/detach.sh" "${pkgdir}/etc/surface-dtx/detach.sh"
24 |
25 | # systemd service files
26 | install -D -m644 "etc/systemd/surface-dtx-daemon.service" "${pkgdir}/usr/lib/systemd/system/surface-dtx-daemon.service"
27 | install -D -m644 "etc/systemd/surface-dtx-userd.service" "${pkgdir}/usr/lib/systemd/user/surface-dtx-userd.service"
28 |
29 | # dbus config file
30 | install -D -m644 "etc/dbus/org.surface.dtx.conf" "${pkgdir}/etc/dbus-1/system.d/org.surface.dtx.conf"
31 |
32 | # udev rules
33 | install -D -m644 "etc/udev/40-surface_dtx.rules" "${pkgdir}/etc/udev/rules.d/40-surface_dtx.rules"
34 |
35 | # completion files
36 | install -D -m644 "target/surface-dtx-daemon.bash" "${pkgdir}/usr/share/bash-completion/completions/surface-dtx-daemon"
37 | install -D -m644 "target/surface-dtx-userd.bash" "${pkgdir}/usr/share/bash-completion/completions/surface-dtx-userd"
38 |
39 | install -D -m644 "target/_surface-dtx-daemon" "${pkgdir}/usr/share/zsh/vendor-completions/_surface-dtx-daemon"
40 | install -D -m644 "target/_surface-dtx-userd" "${pkgdir}/usr/share/zsh/vendor-completions/_surface-dtx-userd"
41 |
42 | install -D -m644 "target/surface-dtx-daemon.fish" "${pkgdir}/usr/share/fish/vendor_completions.d/surface-dtx-daemon.fish"
43 | install -D -m644 "target/surface-dtx-userd.fish" "${pkgdir}/usr/share/fish/vendor_completions.d/surface-dtx-userd.fish"
44 |
45 | %:
46 | dh $@
--------------------------------------------------------------------------------
/pkg/deb/makedeb:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 |
4 | branch="HEAD"
5 | basepath="pkg/deb"
6 |
7 |
8 | pushd() {
9 | command pushd "$@" > /dev/null
10 | }
11 |
12 | popd() {
13 | command popd "$@" > /dev/null
14 | }
15 |
16 |
17 | chkdir() {
18 | if [ ! -d ".git" ]
19 | then
20 | echo "Error: Script must be run from the root directory"
21 | exit 1
22 | fi
23 | }
24 |
25 | prepare() {
26 | archive="src.tar"
27 |
28 | git archive --format tar "$branch" > "$basepath/$archive"
29 |
30 | mkdir -p "$basepath/src"
31 | tar xf "$basepath/$archive" --directory "$basepath/src"
32 |
33 | cp -r "$basepath/debian" "$basepath/src/"
34 | }
35 |
36 | build() {
37 | pushd "$basepath/src"
38 | dpkg-buildpackage -b -d -us -uc
39 | popd
40 | }
41 |
42 |
43 | clean() {
44 | echo "TODO"
45 | }
46 |
47 |
48 | chkdir
49 | prepare
50 | build
51 |
--------------------------------------------------------------------------------
/pkg/fedora/makerpm:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # Default to using the first specfile in the current directory
4 | SPEC="*.spec"
5 | OPTS="-ba"
6 |
7 | SIGN=0
8 | KEY=""
9 |
10 | BUILD=".build"
11 | RPMS="out"
12 |
13 | usage() {
14 | echo "Usage: $0 [OPTION]..."
15 | echo "Wrapper for rpmbuild that is easier to use."
16 | echo
17 | echo "Options:"
18 | echo " -h This help message"
19 | echo " -f The specfile to build from"
20 | echo " -c Clean the build artifacts"
21 | echo " -s Sign the produced RPM packages"
22 | echo " -k The GPG key to use for signing"
23 | exit
24 | }
25 |
26 | clean() {
27 | rm -rf $BUILD
28 | rm -rf $RPMS
29 | exit
30 | }
31 |
32 | while getopts ":hcsf:k:" args; do
33 | case "$args" in
34 | f)
35 | SPEC=$OPTARG
36 | ;;
37 | s)
38 | SIGN=1
39 | ;;
40 | k)
41 | KEY=$OPTARG
42 | ;;
43 | c)
44 | clean
45 | ;;
46 | h)
47 | usage
48 | ;;
49 | esac
50 | done
51 | shift $((OPTIND-1))
52 |
53 | if [ ! "$@" = "" ]; then
54 | OPTS="$@"
55 | fi
56 |
57 | # Check if the specfile exists
58 | if [ "$(ls -f $SPEC | wc -l)" = "0" ]; then
59 | echo "ERROR: No specfile found. Specify it with the -s option."
60 | exit -2
61 | fi
62 |
63 | # Check if there are too many specfiles
64 | if [ ! "$(ls -f $SPEC | wc -l)" = "1" ]; then
65 | echo "ERROR: Ambiguous matches for specfile. Please specify a single" \
66 | "file through the -s option."
67 | exit -7
68 | fi
69 |
70 | # Get the directory of the specfile
71 | SPEC=$(ls -f $SPEC)
72 | DIR=$(readlink -f $(dirname $SPEC))
73 |
74 | if [ ! -d "$DIR/$BUILD" ]; then
75 | mkdir "$DIR/$BUILD"
76 | fi
77 |
78 | FILES=$(find $DIR -maxdepth 1);
79 | for file in $FILES; do
80 | [ "$file" = "$DIR" ] && continue
81 | [ "$file" = "$DIR/$BUILD" ] && continue
82 | [ "$file" = "$DIR/$RPMS" ] && continue
83 |
84 | cp -r "$file" "$DIR/$BUILD"
85 | done
86 |
87 | spectool \
88 | --define "_sourcedir $DIR/$BUILD" \
89 | --define "_builddir $DIR/$BUILD" \
90 | --define "_srcrpmdir $DIR/$RPMS" \
91 | --define "_rpmdir $DIR/$RPMS" \
92 | --define "_specdir $DIR" \
93 | --get-files --all \
94 | --directory $DIR/$BUILD $SPEC
95 |
96 | echo
97 |
98 | rpmbuild \
99 | --define "_sourcedir $DIR/$BUILD" \
100 | --define "_builddir $DIR/$BUILD" \
101 | --define "_srcrpmdir $DIR/$RPMS" \
102 | --define "_rpmdir $DIR/$RPMS" \
103 | --define "_specdir $DIR" \
104 | $OPTS $SPEC
105 |
106 | if [ ! "$SIGN" = "1" ]; then
107 | exit
108 | fi
109 |
110 | for file in $(find out/ -name '*.rpm'); do
111 | echo "Signing $file"
112 | if [ "$KEY" = "" ]; then
113 | rpm --resign $file 2>&1 > /dev/null
114 | else
115 | rpm --resign $file --define "_gpg_name $KEY" 2>&1 > /dev/null
116 | fi
117 | done
118 |
--------------------------------------------------------------------------------
/pkg/fedora/surface-dtx-daemon.spec:
--------------------------------------------------------------------------------
1 | Name: surface-dtx-daemon
2 | Version: 0.3.9
3 | Release: 1%{?dist}
4 | Summary: Surface Detachment System (DTX) Daemon
5 |
6 | License: MIT
7 | URL: https://github.com/linux-surface/surface-dtx-daemon
8 |
9 | Requires: dbus libgcc
10 | BuildRequires: rust cargo dbus-devel
11 |
12 | %global debug_package %{nil}
13 |
14 | %description
15 | Linux User-Space Detachment System (DTX) Daemon for the Surface ACPI Driver
16 | (and Surface Books). Currently only the Surface Book 2 is supported, due to
17 | lack of driver-support on the Surface Book 1. This may change in the future.
18 |
19 | %prep
20 |
21 | %build
22 | export CARGO_TARGET_DIR="$PWD/target"
23 | export CARGO_INCREMENTAL=0
24 |
25 | cargo build --release --locked
26 | strip --strip-all "target/release/surface-dtx-daemon"
27 | strip --strip-all "target/release/surface-dtx-userd"
28 |
29 | %install
30 |
31 | # binary files
32 | install -D -m755 "target/release/surface-dtx-daemon" "%{buildroot}/usr/bin/surface-dtx-daemon"
33 | install -D -m755 "target/release/surface-dtx-userd" "%{buildroot}/usr/bin/surface-dtx-userd"
34 |
35 | # application files
36 | install -D -m644 "target/etc/dtx/surface-dtx-daemon.conf" "%{buildroot}/etc/surface-dtx/surface-dtx-daemon.conf"
37 | install -D -m644 "target/etc/dtx/surface-dtx-userd.conf" "%{buildroot}/etc/surface-dtx/surface-dtx-userd.conf"
38 | install -D -m755 "target/etc/dtx/attach.sh" "%{buildroot}/etc/surface-dtx/attach.sh"
39 | install -D -m755 "target/etc/dtx/detach.sh" "%{buildroot}/etc/surface-dtx/detach.sh"
40 | install -D -m644 "target/etc/systemd/surface-dtx-daemon.service" "%{buildroot}/usr/lib/systemd/system/surface-dtx-daemon.service"
41 | install -D -m644 "target/etc/systemd/surface-dtx-userd.service" "%{buildroot}/usr/lib/systemd/user/surface-dtx-userd.service"
42 | install -D -m644 "target/etc/dbus/org.surface.dtx.conf" "%{buildroot}/etc/dbus-1/system.d/org.surface.dtx.conf"
43 | install -D -m644 "target/etc/udev/40-surface_dtx.rules" "%{buildroot}/etc/udev/rules.d/40-surface_dtx.rules"
44 |
45 | # completion files
46 | install -D -m644 "target/surface-dtx-daemon.bash" "%{buildroot}/usr/share/bash-completion/completions/surface-dtx-daemon"
47 | install -D -m644 "target/surface-dtx-userd.bash" "%{buildroot}/usr/share/bash-completion/completions/surface-dtx-userd"
48 | install -D -m644 "target/_surface-dtx-daemon" "%{buildroot}/usr/share/zsh/site-functions/_surface-dtx-daemon"
49 | install -D -m644 "target/_surface-dtx-userd" "%{buildroot}/usr/share/zsh/site-functions/_surface-dtx-userd"
50 | install -D -m644 "target/surface-dtx-daemon.fish" "%{buildroot}/usr/share/fish/vendor_completions.d/surface-dtx-daemon.fish"
51 | install -D -m644 "target/surface-dtx-userd.fish" "%{buildroot}/usr/share/fish/vendor_completions.d/surface-dtx-userd.fish"
52 |
53 | %files
54 | %config /etc/dbus-1/system.d/org.surface.dtx.conf
55 | %config /etc/udev/rules.d/40-surface_dtx.rules
56 | %config(noreplace) /etc/surface-dtx/*
57 | /usr/bin/surface-dtx-daemon
58 | /usr/bin/surface-dtx-userd
59 | /usr/lib/systemd/system/surface-dtx-daemon.service
60 | /usr/lib/systemd/user/surface-dtx-userd.service
61 | /usr/share/bash-completion/completions/surface-dtx-daemon
62 | /usr/share/bash-completion/completions/surface-dtx-userd
63 | /usr/share/zsh/site-functions/_surface-dtx-daemon
64 | /usr/share/zsh/site-functions/_surface-dtx-userd
65 | /usr/share/fish/vendor_completions.d/surface-dtx-daemon.fish
66 | /usr/share/fish/vendor_completions.d/surface-dtx-userd.fish
67 |
68 | %changelog
69 | * Sat Apr 19 2025 Maximilian Luz - 0.3.9-1
70 | - Update dependencies
71 |
72 | * Sat Sep 14 2024 Maximilian Luz - 0.3.8-1
73 | - Update dependencies
74 |
75 | * Thu Mar 14 2024 Maximilian Luz - 0.3.7-1
76 | - Update dependencies
77 |
78 | * Tue Oct 03 2023 Maximilian Luz - 0.3.6-2
79 | - Bump release for Fedora 39 build
80 |
81 | * Tue Oct 03 2023 Maximilian Luz - 0.3.6-1
82 | - Update dependencies
83 |
84 | * Tue Jul 11 2023 Maximilian Luz - 0.3.5-1
85 | - Update dependencies
86 |
87 | * Wed Apr 19 2023 Maximilian Luz - 0.3.4-1
88 | - Update dependencies
89 |
90 | * Thu Apr 28 2022 Dorian Stoll - 0.3.2-1
91 | - Update dependencies
92 |
93 | * Wed Apr 27 2022 Dorian Stoll - 0.3.1-3
94 | - Bump release to build for Fedora 36
95 |
96 | * Wed Nov 03 2021 Dorian Stoll - 0.3.1-2
97 | - Bump release to build for Fedora 35
98 |
99 | * Mon Aug 23 2021 Maximilian Luz - 0.3.1-1
100 | - Fix typo causing the user-daemon to crash on latch error
101 | - Update dependencies
102 |
103 | * Wed Apr 07 2021 Maximilian Luz - 0.3.0-1
104 | - Properly forward hardware-errors, runtime-errors, and other notifications to user
105 | - Support DTX heartbeat commend
106 | - Support handler script timeouts
107 | - Various stability improvements
108 |
109 | * Fri Mar 19 2021 Dorian Stoll - 0.2.0-2
110 | - Bump release to build for Fedora 34
111 |
112 | * Tue Sep 29 2020 Dorian Stoll - 0.1.5-2
113 | - Bump release to build for Fedora 33
114 |
115 | * Sat Jul 04 2020 Maximilian Luz 0.1.5-1
116 | - Update dependencies
117 |
118 | * Tue Mar 31 2020 Dorian Stoll 0.1.4-3
119 | - Bump pkgrel
120 |
121 | * Fri Sep 27 2019 Dorian Stoll
122 | - Update packaging
123 |
124 | * Sat Sep 14 2019 Dorian Stoll
125 | - Update to 0.1.4
126 |
127 | * Fri May 17 2019 Dorian Stoll
128 | - Initial version
129 |
--------------------------------------------------------------------------------
/surface-dtx-daemon/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "surface-dtx-daemon"
3 | version = "0.3.9"
4 | authors = ["Maximilian Luz "]
5 | description = "Surface Detachment System (DTX) Daemon"
6 |
7 | repository = "https://github.com/linux-surface/surface-dtx-daemon/"
8 | license = "MIT"
9 |
10 | edition = "2018"
11 | build = "build.rs"
12 |
13 | [dependencies]
14 | anyhow = "1.0.98"
15 | clap = { version = "4.5.37", features = ["cargo"] }
16 | dbus = "0.9.7"
17 | dbus-tokio = "0.7.6"
18 | dbus-crossroads = "0.5.2"
19 | futures = "0.3.31"
20 | libc = "0.2.172"
21 | nix = "0.29.0"
22 | sdtx = { git = "https://github.com/linux-surface/libsurfacedtx", tag = "v0.1.6" }
23 | sdtx-tokio = { git = "https://github.com/linux-surface/libsurfacedtx", tag = "v0.1.6" }
24 | serde = { version = "1.0.219", features = ['derive'] }
25 | tokio = { version = "1.44.2", features = ["fs", "sync", "process", "signal", "io-util", "rt", "macros"] }
26 | toml = "0.8.20"
27 | serde_ignored = "0.1.11"
28 | tracing = "0.1.41"
29 | tracing-subscriber = { version = "0.3.19", features = ["std", "env-filter"] }
30 |
31 | [build-dependencies]
32 | clap = "4.5.37"
33 | clap_complete = "4.5.47"
34 |
--------------------------------------------------------------------------------
/surface-dtx-daemon/build.rs:
--------------------------------------------------------------------------------
1 | use std::env;
2 | use std::path::PathBuf;
3 | use clap_complete::shells;
4 |
5 | include!("src/cli.rs");
6 |
7 |
8 | fn main() {
9 | let outdir: PathBuf = env::var_os("CARGO_TARGET_DIR")
10 | .or_else(|| env::var_os("OUT_DIR"))
11 | .unwrap()
12 | .into();
13 |
14 | let rootdir = env::current_dir().unwrap();
15 | let rootdir = rootdir
16 | .parent().unwrap();
17 |
18 | println!("{rootdir:?}");
19 |
20 | // generate shell completions
21 | let mut app = app();
22 | clap_complete::generate_to(shells::Bash, &mut app, "surface-dtx-daemon", &outdir).unwrap();
23 | clap_complete::generate_to(shells::Zsh, &mut app, "surface-dtx-daemon", &outdir).unwrap();
24 | clap_complete::generate_to(shells::Fish, &mut app, "surface-dtx-daemon", &outdir).unwrap();
25 |
26 | // copy config files
27 | let files = [
28 | "etc/dbus/org.surface.dtx.conf",
29 | "etc/dtx/attach.sh",
30 | "etc/dtx/detach.sh",
31 | "etc/dtx/surface-dtx-daemon.conf",
32 | "etc/systemd/surface-dtx-daemon.service",
33 | "etc/udev/40-surface_dtx.rules",
34 | ];
35 |
36 | for file in files {
37 | let src = rootdir.join(file);
38 | let tgt = outdir.join(file);
39 |
40 | std::fs::create_dir_all(tgt.parent().unwrap()).unwrap();
41 | std::fs::copy(src, tgt).unwrap();
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/surface-dtx-daemon/src/cli.rs:
--------------------------------------------------------------------------------
1 | use clap::{Arg, Command, ArgAction};
2 |
3 | pub fn app() -> Command {
4 | Command::new("Surface DTX Daemon")
5 | .about(clap::crate_description!())
6 | .version(clap::crate_version!())
7 | .author(clap::crate_authors!())
8 | .arg(Arg::new("config")
9 | .short('c')
10 | .long("config")
11 | .value_name("FILE")
12 | .help("Use the specified config file")
13 | .value_parser(clap::value_parser!(std::path::PathBuf)))
14 | .arg(Arg::new("no-log-time")
15 | .long("no-log-time")
16 | .help("Do not emit timestamps in log")
17 | .action(ArgAction::SetTrue))
18 | }
19 |
--------------------------------------------------------------------------------
/surface-dtx-daemon/src/config.rs:
--------------------------------------------------------------------------------
1 | use std::collections::BTreeSet;
2 | use std::path::{Path, PathBuf};
3 |
4 | use anyhow::{Context, Result};
5 | use serde::{Deserialize, Serialize};
6 | use tracing::{debug, warn};
7 |
8 |
9 | const DEFAULT_CONFIG_PATH: &str = "/etc/surface-dtx/surface-dtx-daemon.conf";
10 |
11 |
12 | #[derive(Debug, Serialize, Deserialize, Default, Clone)]
13 | pub struct Config {
14 | #[serde(skip)]
15 | pub dir: PathBuf,
16 |
17 | #[serde(default)]
18 | pub log: Log,
19 |
20 | #[serde(default)]
21 | pub handler: Handler,
22 | }
23 |
24 | #[derive(Debug, Serialize, Deserialize, Default, Clone)]
25 | pub struct Log {
26 | #[serde(default)]
27 | pub level: LogLevel,
28 | }
29 |
30 | #[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Default)]
31 | #[serde(rename_all="lowercase")]
32 | pub enum LogLevel {
33 | Error,
34 | Warn,
35 | #[default]
36 | Info,
37 | Debug,
38 | Trace,
39 | }
40 |
41 | #[derive(Debug, Serialize, Deserialize, Default, Clone)]
42 | pub struct Handler {
43 | #[serde(default)]
44 | pub detach: DetachHandler,
45 |
46 | #[serde(default)]
47 | pub detach_abort: DetachAbortHandler,
48 |
49 | #[serde(default)]
50 | pub attach: AttachHandler,
51 | }
52 |
53 | #[derive(Debug, Serialize, Deserialize, Default, Clone)]
54 | pub struct DetachHandler {
55 | #[serde(default)]
56 | pub exec: Option,
57 |
58 | #[serde(default="defaults::task_timeout")]
59 | pub timeout: f32,
60 | }
61 |
62 | #[derive(Debug, Serialize, Deserialize, Default, Clone)]
63 | pub struct DetachAbortHandler {
64 | #[serde(default)]
65 | pub exec: Option,
66 |
67 | #[serde(default="defaults::task_timeout")]
68 | pub timeout: f32,
69 | }
70 |
71 | #[derive(Debug, Serialize, Deserialize, Default, Clone)]
72 | pub struct AttachHandler {
73 | #[serde(default)]
74 | pub exec: Option,
75 |
76 | #[serde(default="defaults::task_timeout")]
77 | pub timeout: f32,
78 |
79 | #[serde(default="defaults::delay_attach")]
80 | pub delay: f32,
81 | }
82 |
83 |
84 | impl Config {
85 | pub fn load() -> Result<(Config, Diagnostics)> {
86 | if Path::new(DEFAULT_CONFIG_PATH).exists() {
87 | Config::load_file(DEFAULT_CONFIG_PATH)
88 | } else {
89 | Ok((Config::default(), Diagnostics::empty()))
90 | }
91 | }
92 |
93 | pub fn load_file>(path: P) -> Result<(Config, Diagnostics)> {
94 | use std::io::Read;
95 |
96 | let mut buf = Vec::new();
97 | let mut file = std::fs::File::open(path.as_ref())
98 | .context("Failed to open config file")?;
99 |
100 | file.read_to_end(&mut buf)
101 | .with_context(|| format!("Failed to read config file (path: {:?})", path.as_ref()))?;
102 |
103 | let data = std::str::from_utf8(&buf)
104 | .with_context(|| format!("Failed to read config file (path: {:?})", path.as_ref()))?;
105 |
106 | let de = toml::Deserializer::new(data);
107 |
108 | let mut unknowns = BTreeSet::new();
109 | let mut config: Config = serde_ignored::deserialize(de, |path| {
110 | unknowns.insert(path.to_string());
111 | }).with_context(|| format!("Failed to read config file (path: {:?})", path.as_ref()))?;
112 |
113 | config.dir = path.as_ref().parent().unwrap().into();
114 |
115 | let diag = Diagnostics {
116 | path: path.as_ref().into(),
117 | unknowns,
118 | };
119 |
120 | Ok((config, diag))
121 | }
122 | }
123 |
124 |
125 | pub struct Diagnostics {
126 | pub path: PathBuf,
127 | pub unknowns: BTreeSet,
128 | }
129 |
130 | impl Diagnostics {
131 | fn empty() -> Self {
132 | Diagnostics {
133 | path: PathBuf::new(),
134 | unknowns: BTreeSet::new()
135 | }
136 | }
137 |
138 | pub fn log(&self) {
139 | let span = tracing::info_span!("config", file=?self.path);
140 | let _guard = span.enter();
141 |
142 | debug!(target: "sdtxd::config", "configuration loaded");
143 | for item in &self.unknowns {
144 | warn!(target: "sdtxd::config", item = %item, "unknown config item")
145 | }
146 | }
147 | }
148 |
149 |
150 | mod defaults {
151 | pub fn delay_attach() -> f32 {
152 | 5.0
153 | }
154 |
155 | pub fn task_timeout() -> f32 {
156 | 60.0
157 | }
158 | }
159 |
160 |
161 | impl From for tracing::Level {
162 | fn from(level: LogLevel) -> Self {
163 | match level {
164 | LogLevel::Error => tracing::Level::ERROR,
165 | LogLevel::Warn => tracing::Level::WARN,
166 | LogLevel::Info => tracing::Level::INFO,
167 | LogLevel::Debug => tracing::Level::DEBUG,
168 | LogLevel::Trace => tracing::Level::TRACE,
169 | }
170 | }
171 | }
172 |
--------------------------------------------------------------------------------
/surface-dtx-daemon/src/logic/mod.rs:
--------------------------------------------------------------------------------
1 | mod core;
2 | pub use self::core::{Adapter, AtHandle, Core, DtHandle, DtcHandle};
3 |
4 | mod proc;
5 | pub use self::proc::ProcessAdapter;
6 |
7 | mod srvc;
8 | pub use self::srvc::ServiceAdapter;
9 |
10 |
11 | use sdtx::event;
12 | pub use sdtx::{BaseInfo, BaseState, DeviceMode, DeviceType, HardwareError, LatchStatus};
13 |
14 |
15 | #[derive(Debug, Clone, Copy, PartialEq, Eq)]
16 | pub enum RuntimeError {
17 | NotAttached,
18 | NotFeasible,
19 | Timeout,
20 | Unknown(u8),
21 | }
22 |
23 | impl From for RuntimeError {
24 | fn from(err: sdtx::RuntimeError) -> Self {
25 | match err {
26 | sdtx::RuntimeError::NotFeasible => Self::NotFeasible,
27 | sdtx::RuntimeError::Timeout => Self::Timeout,
28 | sdtx::RuntimeError::Unknown(x) => Self::Unknown(x),
29 | }
30 | }
31 | }
32 |
33 | impl std::fmt::Display for RuntimeError {
34 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35 | match self {
36 | Self::NotAttached => write!(f, "no base attached"),
37 | Self::NotFeasible => write!(f, "not feasible"),
38 | Self::Timeout => write!(f, "timeout"),
39 | Self::Unknown(x) => write!(f, "unknown: {x:#04x}"),
40 | }
41 | }
42 | }
43 |
44 |
45 | #[derive(Debug, Clone, Copy, PartialEq, Eq)]
46 | pub enum CancelReason {
47 | UserRequest, // user or higher layer requested cancelation, or user did not act
48 | HandlerTimeout,
49 | DisconnectTimeout,
50 | Runtime(RuntimeError),
51 | Hardware(HardwareError),
52 | Unknown(u16),
53 | }
54 |
55 | impl From for CancelReason {
56 | fn from(reason: event::CancelReason) -> Self {
57 | match reason {
58 | event::CancelReason::Runtime(e) => Self::Runtime(RuntimeError::from(e)),
59 | event::CancelReason::Hardware(e) => Self::Hardware(e),
60 | event::CancelReason::Unknown(x) => Self::Unknown(x),
61 | }
62 | }
63 | }
64 |
65 | impl std::fmt::Display for CancelReason {
66 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67 | match self {
68 | Self::UserRequest => write!(f, "user request"),
69 | Self::HandlerTimeout => write!(f, "timed out waiting for detachment handler"),
70 | Self::DisconnectTimeout => write!(f, "timed out waiting for user to disconnect base"),
71 | Self::Runtime(err) => write!(f, "runtime error: {err}"),
72 | Self::Hardware(err) => write!(f, "hardware error: {err}"),
73 | Self::Unknown(x) => write!(f, "unknown: {x:#04x}"),
74 | }
75 | }
76 | }
77 |
78 |
79 | #[derive(Debug, Clone, Copy, PartialEq, Eq)]
80 | pub enum LatchState {
81 | Closed,
82 | Opened,
83 | }
84 |
85 | impl From for LatchStatus {
86 | fn from(status: LatchState) -> Self {
87 | match status {
88 | LatchState::Closed => Self::Closed,
89 | LatchState::Opened => Self::Opened,
90 | }
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/surface-dtx-daemon/src/logic/proc.rs:
--------------------------------------------------------------------------------
1 | use crate::config::Config;
2 | use crate::logic::{
3 | Adapter,
4 | AtHandle,
5 | DtHandle,
6 | DtcHandle,
7 | };
8 | use crate::utils::taskq::TaskSender;
9 |
10 | use std::time::Duration;
11 |
12 | use anyhow::{Context, Error, Result};
13 | use tokio::process::Command;
14 | use tracing::{Level, debug, trace};
15 |
16 |
17 | const HEARTBEAT_PERIOD_MS: u64 = 2500;
18 |
19 |
20 | #[derive(Debug, Clone, Copy, PartialEq, Eq)]
21 | enum ExitStatus {
22 | Commence = 0,
23 | Abort = 1,
24 | }
25 |
26 | impl ExitStatus {
27 | fn as_str(&self) -> &'static str {
28 | match self {
29 | Self::Commence => "0",
30 | Self::Abort => "1",
31 | }
32 | }
33 | }
34 |
35 | impl From for ExitStatus {
36 | fn from(status: std::process::ExitStatus) -> Self {
37 | status.code().map(|s| if s == 0 {
38 | ExitStatus::Commence
39 | } else {
40 | ExitStatus::Abort
41 | }).unwrap_or(ExitStatus::Abort)
42 | }
43 | }
44 |
45 |
46 | pub struct ProcessAdapter {
47 | config: Config,
48 | queue: TaskSender,
49 | }
50 |
51 | impl ProcessAdapter {
52 | pub fn new(config: Config, queue: TaskSender) -> Self {
53 | Self {
54 | config,
55 | queue,
56 | }
57 | }
58 | }
59 |
60 | impl Adapter for ProcessAdapter {
61 | fn detachment_start(&mut self, handle: DtHandle) -> Result<()> {
62 | // build heartbeat task
63 | let h = handle.clone();
64 | let heartbeat = async move {
65 | loop {
66 | tokio::time::sleep(Duration::from_millis(HEARTBEAT_PERIOD_MS)).await;
67 | h.heartbeat()?;
68 | }
69 | };
70 |
71 | // build timeout task
72 | let h = handle.clone();
73 | let timeout = self.config.handler.detach.timeout * 1000.0;
74 | let timeout = async move {
75 | tokio::time::sleep(Duration::from_millis(timeout as _)).await;
76 |
77 | trace!(target: "sdtxd::proc", "detachment process timed out, canceling");
78 | h.timeout();
79 |
80 | Ok(())
81 | };
82 |
83 | // build process task
84 | let dir = self.config.dir.clone();
85 | let handler = self.config.handler.detach.exec.clone();
86 | let proc = async move {
87 | trace!(target: "sdtxd::proc", "detachment process started");
88 |
89 | // run handler if specified
90 | let status = if let Some(ref path) = handler {
91 | debug!(target: "sdtxd::proc", ?path, ?dir, "running detachment handler");
92 |
93 | // run handler
94 | let output = Command::new(path)
95 | .current_dir(dir)
96 | .env("EXIT_DETACH_COMMENCE", ExitStatus::Commence.as_str())
97 | .env("EXIT_DETACH_ABORT", ExitStatus::Abort.as_str())
98 | .kill_on_drop(true)
99 | .output().await
100 | .context("Subprocess error (detachment)")?;
101 |
102 | // log output
103 | output.log("detachment handler");
104 |
105 | // confirm latch open/detach commence based on return status
106 | ExitStatus::from(output.status)
107 |
108 | } else {
109 | debug!(target: "sdtxd::proc", "no detachment handler specified, skipping");
110 | ExitStatus::Commence
111 | };
112 |
113 | // send response, will be ignored if already canceled
114 | if status == ExitStatus::Commence {
115 | debug!(target: "sdtxd::proc", "detachment commencing based on handler response");
116 | handle.confirm();
117 | } else {
118 | debug!(target: "sdtxd::proc", "detachment canceled based on handler response");
119 | handle.cancel();
120 | }
121 |
122 | trace!(target: "sdtxd::proc", "detachment process completed");
123 | Ok(())
124 | };
125 |
126 | // build task
127 | let task = async move {
128 | tokio::select! {
129 | r = proc => r,
130 | r = heartbeat => r,
131 | r = timeout => r,
132 | }
133 | };
134 |
135 | // submit task
136 | trace!(target: "sdtxd::proc", "scheduling detachment task");
137 | if self.queue.submit(task).is_err() {
138 | unreachable!("receiver dropped");
139 | }
140 |
141 | Ok(())
142 | }
143 |
144 | fn detachment_cancel_start(&mut self, handle: DtcHandle) -> Result<()> {
145 | // build timeout task
146 | let h = handle.clone();
147 | let timeout = self.config.handler.detach_abort.timeout * 1000.0;
148 | let timeout = async move {
149 | tokio::time::sleep(Duration::from_millis(timeout as _)).await;
150 |
151 | trace!(target: "sdtxd::proc", "detachment-abort timed out, canceling");
152 | h.timeout();
153 |
154 | Ok(())
155 | };
156 |
157 | // build process task
158 | let dir = self.config.dir.clone();
159 | let handler = self.config.handler.detach_abort.exec.clone();
160 | let proc = async move {
161 | trace!(target: "sdtxd::proc", "detachment-abort process started");
162 |
163 | // run handler if specified
164 | if let Some(ref path) = handler {
165 | debug!(target: "sdtxd::proc", ?path, ?dir, "running detachment-abort handler");
166 |
167 | // run handler
168 | let output = Command::new(path)
169 | .current_dir(dir)
170 | .kill_on_drop(true)
171 | .output().await
172 | .context("Subprocess error (detachment-abort)")?;
173 |
174 | // log output
175 | output.log("detachment-abort handler");
176 |
177 | } else {
178 | debug!(target: "sdtxd::proc", "no detachment-abort handler specified, skipping");
179 | };
180 |
181 | trace!(target: "sdtxd::proc", "detachment-abort process completed");
182 | handle.complete();
183 |
184 | Ok(())
185 | };
186 |
187 | // build task
188 | let task = async move {
189 | tokio::select! {
190 | r = proc => r,
191 | r = timeout => r,
192 | }
193 | };
194 |
195 | // submit task
196 | trace!(target: "sdtxd::proc", "scheduling detachment-abort task");
197 | if self.queue.submit(task).is_err() {
198 | unreachable!("receiver dropped");
199 | }
200 |
201 | Ok(())
202 | }
203 |
204 | fn attachment_start(&mut self, handle: AtHandle) -> Result<()> {
205 | // build timeout task
206 | let h = handle.clone();
207 | let timeout = self.config.handler.attach.timeout * 1000.0;
208 | let timeout = async move {
209 | tokio::time::sleep(Duration::from_millis(timeout as _)).await;
210 |
211 | trace!(target: "sdtxd::proc", "detachment-abort timed out, canceling");
212 | h.timeout();
213 |
214 | Ok(())
215 | };
216 |
217 | // build process task
218 | let dir = self.config.dir.clone();
219 | let handler = self.config.handler.attach.exec.clone();
220 | let proc = async move {
221 | trace!(target: "sdtxd::proc", "attachment process started");
222 |
223 | // run handler if specified
224 | if let Some(ref path) = handler {
225 | debug!(target: "sdtxd::proc", ?path, ?dir, "running attachment handler");
226 |
227 | // run handler
228 | let output = Command::new(path)
229 | .current_dir(dir)
230 | .kill_on_drop(true)
231 | .output().await
232 | .context("Subprocess error (attachment)")?;
233 |
234 | // log output
235 | output.log("attachment handler");
236 |
237 | } else {
238 | debug!(target: "sdtxd::proc", "no attachment handler specified, skipping");
239 | };
240 |
241 | trace!(target: "sdtxd::proc", "attachment process completed");
242 | handle.complete();
243 |
244 | Ok(())
245 | };
246 |
247 | // build task
248 | let delay = Duration::from_millis((self.config.handler.attach.delay * 1000.0) as _);
249 | let task = async move {
250 | // delay to ensure all devices are set up
251 | debug!(target: "sdtxd::proc", "delaying attachment process by {}ms", delay.as_millis());
252 | tokio::time::sleep(delay).await;
253 |
254 | // drive main tasks
255 | tokio::select! {
256 | r = proc => r,
257 | r = timeout => r,
258 | }
259 | };
260 |
261 | // submit task
262 | trace!(target: "sdtxd::proc", "scheduling attachment task");
263 | if self.queue.submit(task).is_err() {
264 | unreachable!("receiver dropped");
265 | }
266 |
267 | Ok(())
268 | }
269 | }
270 |
271 |
272 | trait ProcessOutputExt {
273 | fn log>(&self, procname: S);
274 | }
275 |
276 | impl ProcessOutputExt for std::process::Output {
277 | fn log>(&self, procname: S) {
278 |
279 | fn log_stream(level: Level, name: &'static str, data: &[u8]) {
280 | if !data.is_empty() {
281 | event!(target: "sdtxd::proc", level, " (contd.)");
282 | event!(target: "sdtxd::proc", level, " (contd.) {}:", name);
283 |
284 | let data = std::str::from_utf8(data);
285 | match data {
286 | Ok(str) => {
287 | for line in str.lines() {
288 | event!(target: "sdtxd::proc", level, " (contd.) {}", line);
289 | }
290 | },
291 | Err(_) => {
292 | event!(target: "sdtxd::proc", level, " (contd.) {:?}", data);
293 | },
294 | }
295 | }
296 | }
297 |
298 | let level = if !self.stderr.is_empty() {
299 | tracing::Level::WARN
300 | } else if !self.stdout.is_empty() {
301 | tracing::Level::INFO
302 | } else {
303 | tracing::Level::DEBUG
304 | };
305 |
306 | event!(target: "sdtxd::proc", level, "{} exited with {}", procname.as_ref(), self.status);
307 | log_stream(level, "stdout", &self.stdout);
308 | log_stream(level, "stderr", &self.stderr);
309 | }
310 | }
311 |
--------------------------------------------------------------------------------
/surface-dtx-daemon/src/logic/srvc.rs:
--------------------------------------------------------------------------------
1 | use crate::logic::{
2 | Adapter,
3 | AtHandle,
4 | BaseInfo,
5 | CancelReason,
6 | DeviceMode,
7 | DtHandle,
8 | DtcHandle,
9 | LatchState,
10 | LatchStatus,
11 | };
12 | use crate::service::{ServiceHandle, Event};
13 |
14 | use anyhow::Result;
15 |
16 |
17 | pub struct ServiceAdapter {
18 | service: ServiceHandle,
19 | }
20 |
21 | impl ServiceAdapter {
22 | pub fn new(service: ServiceHandle) -> Self {
23 | Self { service }
24 | }
25 | }
26 |
27 | impl Adapter for ServiceAdapter {
28 | fn set_state(&mut self, mode: DeviceMode, base: BaseInfo, latch: LatchState) {
29 | self.service.set_base_info(base);
30 | self.service.set_latch_status(latch.into());
31 | self.service.set_device_mode(mode);
32 | }
33 |
34 | fn on_base_state(&mut self, info: BaseInfo) -> Result<()> {
35 | self.service.set_base_info(info);
36 | Ok(())
37 | }
38 |
39 | fn on_latch_status(&mut self, status: LatchStatus) -> Result<()> {
40 | self.service.set_latch_status(status);
41 | Ok(())
42 | }
43 |
44 | fn on_device_mode(&mut self, mode: DeviceMode) -> Result<()> {
45 | self.service.set_device_mode(mode);
46 | Ok(())
47 | }
48 |
49 | fn request_inhibited(&mut self, reason: CancelReason) -> Result<()> {
50 | self.service.emit_event(Event::DetachmentInhibited { reason });
51 | Ok(())
52 | }
53 |
54 | fn detachment_start(&mut self, _handle: DtHandle) -> Result<()> {
55 | self.service.emit_event(Event::DetachmentStart);
56 | Ok(())
57 | }
58 |
59 | fn detachment_ready(&mut self) -> Result<()> {
60 | self.service.emit_event(Event::DetachmentReady);
61 | Ok(())
62 | }
63 |
64 | fn detachment_complete(&mut self) -> Result<()> {
65 | self.service.emit_event(Event::DetachmentComplete);
66 | Ok(())
67 | }
68 |
69 | fn detachment_cancel(&mut self, reason: CancelReason) -> Result<()> {
70 | self.service.emit_event(Event::DetachmentCancel { reason });
71 | Ok(())
72 | }
73 |
74 | fn detachment_cancel_start(&mut self, _handle: DtcHandle) -> Result<()> {
75 | self.service.emit_event(Event::DetachmentCancelStart);
76 | Ok(())
77 | }
78 |
79 | fn detachment_cancel_complete(&mut self) -> Result<()> {
80 | self.service.emit_event(Event::DetachmentCancelComplete);
81 | Ok(())
82 | }
83 |
84 | fn detachment_cancel_timeout(&mut self) -> Result<()> {
85 | self.service.emit_event(Event::DetachmentCancelTimeout);
86 | Ok(())
87 | }
88 |
89 | fn detachment_unexpected(&mut self) -> Result<()> {
90 | self.service.emit_event(Event::DetachmentUnexpected);
91 | Ok(())
92 | }
93 |
94 | fn attachment_start(&mut self, _handle: AtHandle) -> Result<()> {
95 | self.service.emit_event(Event::AttachmentStart);
96 | Ok(())
97 | }
98 |
99 | fn attachment_complete(&mut self) -> Result<()> {
100 | self.service.emit_event(Event::AttachmentComplete);
101 | Ok(())
102 | }
103 |
104 | fn attachment_timeout(&mut self) -> Result<()> {
105 | self.service.emit_event(Event::AttachmentTimeout);
106 | Ok(())
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/surface-dtx-daemon/src/main.rs:
--------------------------------------------------------------------------------
1 | #[macro_use]
2 | mod utils;
3 | use utils::task::JoinHandleExt;
4 |
5 | mod cli;
6 |
7 | mod config;
8 | use config::Config;
9 |
10 | mod logic;
11 |
12 | mod service;
13 | use service::Service;
14 |
15 |
16 | use std::{sync::{Arc, Mutex}, path::PathBuf, io::IsTerminal};
17 |
18 | use anyhow::{Context, Result};
19 |
20 | use dbus::channel::MatchingReceiver;
21 | use dbus::message::MatchRule;
22 | use dbus_tokio::connection;
23 | use dbus_crossroads::Crossroads;
24 |
25 | use futures::prelude::*;
26 |
27 | use tokio::signal::unix::{signal, SignalKind};
28 |
29 | use tracing::{error, info, trace, warn};
30 |
31 |
32 | fn bootstrap() -> Result {
33 | // handle command line input
34 | let matches = cli::app().get_matches();
35 |
36 | // set up config
37 | let (config, diag) = match matches.get_one::("config") {
38 | Some(path) => Config::load_file(path)?,
39 | None => Config::load()?,
40 | };
41 |
42 | // set up logger
43 | let filter = tracing_subscriber::EnvFilter::from_env("SDTXD_LOG")
44 | .add_directive(tracing::Level::from(config.log.level).into());
45 |
46 | let fmt = tracing_subscriber::fmt::format::PrettyFields::new();
47 |
48 | let subscriber = tracing_subscriber::fmt()
49 | .fmt_fields(fmt)
50 | .with_env_filter(filter)
51 | .with_ansi(std::io::stdout().is_terminal());
52 |
53 | if matches.get_flag("no-log-time") {
54 | subscriber.without_time().init();
55 | } else {
56 | subscriber.init();
57 | }
58 |
59 | // warn about unknown config items
60 | diag.log();
61 |
62 | Ok(config)
63 | }
64 |
65 | async fn run() -> Result<()> {
66 | let config = bootstrap()?;
67 |
68 | // set up signal handling
69 | trace!(target: "sdtxd", "setting up signal handling");
70 |
71 | let mut sigint = signal(SignalKind::interrupt()).context("Failed to set up signal handling")?;
72 | let mut sigterm = signal(SignalKind::terminate()).context("Failed to set up signal handling")?;
73 |
74 | let sig = async { tokio::select! {
75 | _ = sigint.recv() => "SIGINT",
76 | _ = sigterm.recv() => "SIGTERM",
77 | }};
78 |
79 | // prepare devices
80 | trace!(target: "sdtxd", "preparing devices");
81 |
82 | let event_device = sdtx_tokio::connect().await
83 | .context("Failed to access DTX device")?;
84 |
85 | let control_device = sdtx_tokio::connect().await
86 | .context("Failed to access DTX device")?;
87 |
88 | // set up D-Bus connection
89 | trace!(target: "sdtxd", "connecting to D-Bus");
90 |
91 | let (dbus_rsrc, dbus_conn) = connection::new_system_sync()
92 | .context("Failed to connect to D-Bus")?;
93 |
94 | let dbus_rsrc = dbus_rsrc.map(|e| Err(e).context("D-Bus connection error"));
95 | let mut dbus_task = tokio::spawn(dbus_rsrc).guard();
96 |
97 | // set up D-Bus service
98 | trace!(target: "sdtxd", "setting up D-Bus service");
99 |
100 | let dbus_cr = Arc::new(Mutex::new(Crossroads::new()));
101 |
102 | let serv = Service::new(dbus_conn.clone(), control_device);
103 | serv.request_name().await?;
104 | serv.register(&mut dbus_cr.lock().unwrap())?;
105 |
106 | let cr = dbus_cr.clone();
107 | let token = dbus_conn.start_receive(MatchRule::new_method_call(), Box::new(move |msg, conn| {
108 | // Crossroads::handle_message() only fails if message is not a method call
109 | cr.lock().unwrap().handle_message(msg, conn).unwrap();
110 | true
111 | }));
112 |
113 | let recv_guard = utils::scope::guard(|| { let _ = dbus_conn.stop_receive(token).unwrap(); });
114 | let serv_guard = utils::scope::guard(|| { serv.unregister(&mut dbus_cr.lock().unwrap()); });
115 |
116 | // set up task-queue
117 | trace!(target: "sdtxd", "setting up task queue");
118 |
119 | let (mut queue, queue_tx) = utils::taskq::new();
120 | let mut queue_task = tokio::spawn(async move { queue.run().await }).guard();
121 |
122 | // set up event handler
123 | trace!(target: "sdtxd", "setting up DTX event handling");
124 |
125 | let proc_adp = logic::ProcessAdapter::new(config, queue_tx);
126 | let srvc_adp = logic::ServiceAdapter::new(serv.handle());
127 |
128 | let mut core = logic::Core::new(event_device, (proc_adp, srvc_adp));
129 | let mut event_task = tokio::spawn(async move { core.run().await }).guard();
130 |
131 | // collect main driver tasks
132 | let tasks = async { tokio::select! {
133 | result = &mut dbus_task => result,
134 | result = &mut event_task => result,
135 | result = &mut queue_task => result,
136 | }};
137 |
138 | // run until whatever comes first: error, panic, or shutdown signal
139 | info!(target: "sdtxd", "running...");
140 |
141 | tokio::select! {
142 | signame = sig => {
143 | // first shutdown signal: try to do a clean shutdown and complete
144 | // the task queue
145 | info!(target: "sdtxd", "received {}, shutting down...", signame);
146 |
147 | // stop event task: don't handle any new DTX events and drop task
148 | // queue transmitter to eventually cause the task queue task to
149 | // complete
150 | event_task.abort();
151 |
152 | // unregister service
153 | drop(serv_guard);
154 |
155 | // stop D-Bus message handling
156 | drop(recv_guard);
157 |
158 | // pepare handling for second shutdown signal
159 | let sig = async { tokio::select! {
160 | _ = sigint.recv() => ("SIGINT", 2),
161 | _ = sigterm.recv() => ("SIGTERM", 15),
162 | }};
163 |
164 | // try to run task queue to completion, shut down and exit if
165 | // second signal received
166 | tokio::select! {
167 | (signame, tval) = sig => {
168 | warn!(target: "sdtxd", "received {} during shutdown, terminating...", signame);
169 | std::process::exit(128 + tval)
170 | },
171 | result = queue_task => match result {
172 | Ok(res) => res,
173 | Err(e) if e.is_panic() => std::panic::resume_unwind(e.into_panic()),
174 | Err(_) => unreachable!("Task unexpectedly canceled"),
175 | }
176 | }
177 | }
178 | result = tasks => match result {
179 | Ok(res) => res,
180 | Err(e) if e.is_panic() => std::panic::resume_unwind(e.into_panic()),
181 | Err(_) => unreachable!("Task unexpectedly canceled"),
182 | },
183 | }
184 | }
185 |
186 | #[tokio::main(flavor = "current_thread")]
187 | async fn main() -> Result<()> {
188 | // run main function and log critical errors
189 | let result = run().await;
190 | if let Err(ref err) = result {
191 | error!(target: "sdtxd", "critical error: {}\n", err);
192 | }
193 |
194 | // for some reason tokio won't properly shut down, even though every task
195 | // we spawned should be either canceled or completed by now...
196 | if let Err(err) = result {
197 | eprintln!("{err:?}");
198 | std::process::exit(1)
199 | } else {
200 | std::process::exit(0)
201 | }
202 | }
203 |
--------------------------------------------------------------------------------
/surface-dtx-daemon/src/service/arg.rs:
--------------------------------------------------------------------------------
1 | use crate::logic::{
2 | BaseInfo,
3 | BaseState,
4 | CancelReason,
5 | DeviceMode,
6 | DeviceType,
7 | HardwareError,
8 | LatchStatus,
9 | RuntimeError,
10 | };
11 |
12 | use dbus::arg::Variant;
13 |
14 |
15 | pub trait DbusArg {
16 | type Arg: dbus::arg::RefArg + 'static;
17 |
18 | fn as_arg(&self) -> Self::Arg;
19 |
20 | fn as_variant(&self) -> Variant> {
21 | Variant(Box::new(self.as_arg()))
22 | }
23 | }
24 |
25 | impl DbusArg for DeviceMode {
26 | type Arg = String;
27 |
28 | fn as_arg(&self) -> String {
29 | match self {
30 | DeviceMode::Tablet => "tablet",
31 | DeviceMode::Laptop => "laptop",
32 | DeviceMode::Studio => "studio",
33 | }.into()
34 | }
35 | }
36 |
37 | impl DbusArg for LatchStatus {
38 | type Arg = String;
39 |
40 | fn as_arg(&self) -> String {
41 | match self {
42 | LatchStatus::Closed => "closed".into(),
43 | LatchStatus::Opened => "opened".into(),
44 | LatchStatus::Error(error) => match error {
45 | HardwareError::FailedToOpen => "error:hardware:failed-to-open".into(),
46 | HardwareError::FailedToRemainOpen => "error:hardware:failed-to-remain-open".into(),
47 | HardwareError::FailedToClose => "error:hardware:failed-to-close".into(),
48 | HardwareError::Unknown(x) => format!("error:hardware:unknown:{x}"),
49 | },
50 | }
51 | }
52 | }
53 |
54 | impl DbusArg for BaseInfo {
55 | type Arg = (String, String, u8);
56 |
57 | fn as_arg(&self) -> Self::Arg {
58 | (self.state.as_arg(), self.device_type.as_arg(), self.id)
59 | }
60 | }
61 |
62 | impl DbusArg for BaseState {
63 | type Arg = String;
64 |
65 | fn as_arg(&self) -> Self::Arg {
66 | match self {
67 | BaseState::Detached => "detached",
68 | BaseState::Attached => "attached",
69 | BaseState::NotFeasible => "not-feasible",
70 | }.into()
71 | }
72 | }
73 |
74 | impl DbusArg for DeviceType {
75 | type Arg = String;
76 |
77 | fn as_arg(&self) -> Self::Arg {
78 | match self {
79 | DeviceType::Hid => "hid".into(),
80 | DeviceType::Ssh => "ssh".into(),
81 | DeviceType::Unknown(x) => format!("unknown:{x}"),
82 | }
83 | }
84 | }
85 |
86 | impl DbusArg for CancelReason {
87 | type Arg = String;
88 |
89 | fn as_arg(&self) -> Self::Arg {
90 | match self {
91 | CancelReason::UserRequest => "request".into(),
92 | CancelReason::HandlerTimeout => "timeout:handler".into(),
93 | CancelReason::DisconnectTimeout => "timeout:disconnect".into(),
94 | CancelReason::Runtime(rt) => match rt {
95 | RuntimeError::NotAttached => "error:runtime:not-attached".into(),
96 | RuntimeError::NotFeasible => "error:runtime:not-feasible".into(),
97 | RuntimeError::Timeout => "error:runtime:timeout".into(),
98 | RuntimeError::Unknown(x) => format!("error:runtime:unknown:{x}"),
99 | },
100 | CancelReason::Hardware(hw) => match hw {
101 | HardwareError::FailedToOpen => "error:hardware:failed-to-open".into(),
102 | HardwareError::FailedToRemainOpen => "error:hardware:failed-to-remain-open".into(),
103 | HardwareError::FailedToClose => "error:hardware:failed-to-close".into(),
104 | HardwareError::Unknown(x) => format!("error:hardware:unknown:{x}"),
105 | },
106 | CancelReason::Unknown(x) => format!("unknown:{x}"),
107 | }
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/surface-dtx-daemon/src/service/event.rs:
--------------------------------------------------------------------------------
1 | use crate::logic::CancelReason;
2 | use crate::service::arg::DbusArg;
3 |
4 | use dbus::arg::{Append, Dict, RefArg, Variant};
5 |
6 |
7 | #[derive(Debug, Clone, Copy)]
8 | pub enum Event {
9 | DetachmentInhibited { reason: CancelReason },
10 | DetachmentStart,
11 | DetachmentReady,
12 | DetachmentComplete,
13 | DetachmentCancel { reason: CancelReason },
14 | DetachmentCancelStart,
15 | DetachmentCancelComplete,
16 | DetachmentCancelTimeout,
17 | DetachmentUnexpected,
18 | AttachmentStart,
19 | AttachmentComplete,
20 | AttachmentTimeout,
21 | }
22 |
23 | impl dbus::arg::AppendAll for Event {
24 | fn append(&self, ia: &mut dbus::arg::IterAppend) {
25 | match self {
26 | Self::DetachmentInhibited { reason } => append1(ia, "detachment:inhibited", "reason", reason),
27 | Self::DetachmentStart => append0(ia, "detachment:start"),
28 | Self::DetachmentReady => append0(ia, "detachment:ready"),
29 | Self::DetachmentComplete => append0(ia, "detachment:complete"),
30 | Self::DetachmentCancel { reason } => append1(ia, "detachment:cancel", "reason", reason),
31 | Self::DetachmentCancelStart => append0(ia, "detachment:cancel:start"),
32 | Self::DetachmentCancelComplete => append0(ia, "detachment:cancel:complete"),
33 | Self::DetachmentCancelTimeout => append0(ia, "detachment:cancel:timeout"),
34 | Self::DetachmentUnexpected => append0(ia, "detachment:unexpected"),
35 | Self::AttachmentStart => append0(ia, "attachment:start"),
36 | Self::AttachmentComplete => append0(ia, "attachment:complete"),
37 | Self::AttachmentTimeout => append0(ia, "attachment:timeout"),
38 | }
39 | }
40 | }
41 |
42 | fn append0(ia: &mut dbus::arg::IterAppend, ty: &'static str) {
43 | let values: Dict>, _> = Dict::new(std::iter::empty());
44 |
45 | ty.append(ia);
46 | values.append(ia);
47 | }
48 |
49 | fn append1(ia: &mut dbus::arg::IterAppend, ty: &'static str, name: &'static str, value: &T)
50 | where
51 | T: DbusArg,
52 | {
53 | ty.append(ia);
54 |
55 | ia.append_dict(&"s".into(), &"v".into(), |ia| {
56 | ia.append_dict_entry(|ia| {
57 | ia.append(name.to_owned());
58 | ia.append(value.as_variant());
59 | })
60 | });
61 | }
62 |
--------------------------------------------------------------------------------
/surface-dtx-daemon/src/service/mod.rs:
--------------------------------------------------------------------------------
1 | mod arg;
2 | use arg::DbusArg;
3 |
4 | mod event;
5 | pub use event::Event;
6 |
7 | mod prop;
8 | use prop::Property;
9 |
10 |
11 | use crate::logic::{
12 | BaseInfo,
13 | BaseState,
14 | DeviceMode,
15 | DeviceType,
16 | LatchStatus,
17 | };
18 |
19 | use std::collections::HashMap;
20 | use std::sync::Arc;
21 |
22 | use anyhow::{Context, Result};
23 |
24 | use dbus::{Message, arg::{RefArg, Variant}};
25 | use dbus::nonblock::SyncConnection;
26 | use dbus_crossroads::{Crossroads, IfaceBuilder, MethodErr};
27 |
28 | use sdtx_tokio::Device;
29 |
30 | use tracing::trace;
31 |
32 |
33 | pub struct Service {
34 | conn: Arc,
35 | inner: Arc,
36 | }
37 |
38 | impl Service {
39 | const PATH: &'static str = "/org/surface/dtx";
40 | const INTERFACE: &'static str = "org.surface.dtx";
41 |
42 | pub fn new(conn: Arc, device: Device) -> Self {
43 | Self { conn, inner: Arc::new(Shared::new(device)) }
44 | }
45 |
46 | pub async fn request_name(&self) -> Result<()> {
47 | self.conn.request_name(Self::INTERFACE, false, true, false).await
48 | .context("Failed to set up D-Bus service")
49 | .map(|_| ())
50 | }
51 |
52 | pub fn register(&self, cr: &mut Crossroads) -> Result<()> {
53 | let iface_token = cr.register(Self::INTERFACE, |b: &mut IfaceBuilder>| {
54 | // device-mode property
55 | b.property("DeviceMode")
56 | .emits_changed_true()
57 | .get(|_, service| { Ok(service.device_mode.as_arg()) });
58 |
59 | // latch status
60 | b.property("LatchStatus")
61 | .emits_changed_true()
62 | .get(|_, service| Ok(service.latch_status.as_arg()));
63 |
64 | // base info
65 | b.property("Base")
66 | .emits_changed_true()
67 | .get(|_, service| Ok(service.base_info.as_arg()));
68 |
69 | // request method
70 | b.method("Request", (), (), move |_ctx, service, _args: ()| {
71 | match service.device.latch_request() {
72 | Ok(()) => { Ok(()) },
73 | Err(e) => { Err(MethodErr::failed(&e)) },
74 | }
75 | });
76 |
77 | // event signal
78 | b.signal::<(String, HashMap>>), _>
79 | ("Event", ("type", "values"));
80 | });
81 |
82 | cr.insert(Self::PATH, &[iface_token], self.inner.clone());
83 | Ok(())
84 | }
85 |
86 | pub fn unregister(&self, cr: &mut Crossroads) {
87 | let _ : Option> = cr.remove(&Self::PATH.into());
88 | }
89 |
90 | pub fn handle(&self) -> ServiceHandle {
91 | ServiceHandle { conn: self.conn.clone(), inner: self.inner.clone() }
92 | }
93 | }
94 |
95 |
96 | #[derive(Clone)]
97 | pub struct ServiceHandle {
98 | conn: Arc,
99 | inner: Arc,
100 | }
101 |
102 | impl ServiceHandle {
103 | pub fn set_device_mode(&self, value: DeviceMode) {
104 | self.inner.device_mode.set(self.conn.as_ref(), value);
105 | }
106 |
107 | pub fn set_latch_status(&self, value: LatchStatus) {
108 | self.inner.latch_status.set(self.conn.as_ref(), value);
109 | }
110 |
111 | pub fn set_base_info(&self, value: BaseInfo) {
112 | self.inner.base_info.set(self.conn.as_ref(), value);
113 | }
114 |
115 | pub fn emit_event(&self, event: Event) {
116 | use dbus::channel::Sender;
117 |
118 | let path = Service::PATH.into();
119 | let interface = Service::INTERFACE.into();
120 |
121 | // build signal message
122 | let mut signal = Message::signal(&path, &interface, &"Event".into());
123 | signal.append_all(event);
124 |
125 | trace!(target: "sdtxd::srvc", object=Service::PATH, interface=Service::INTERFACE,
126 | value=?event, "emmiting event");
127 |
128 | // only fails when memory runs out
129 | self.conn.send(signal).unwrap();
130 | }
131 | }
132 |
133 |
134 | struct Shared {
135 | device: Device,
136 | device_mode: Property,
137 | latch_status: Property,
138 | base_info: Property,
139 | }
140 |
141 | impl Shared {
142 | fn new(device: Device) -> Self {
143 | let base = BaseInfo {
144 | state: BaseState::Attached,
145 | device_type: DeviceType::Ssh,
146 | id: 0,
147 | };
148 |
149 | Self {
150 | device,
151 | device_mode: Property::new("DeviceMode", DeviceMode::Laptop),
152 | latch_status: Property::new("LatchStatus", LatchStatus::Closed),
153 | base_info: Property::new("Base", base),
154 | }
155 | }
156 | }
157 |
--------------------------------------------------------------------------------
/surface-dtx-daemon/src/service/prop.rs:
--------------------------------------------------------------------------------
1 | use crate::service::Service;
2 | use crate::service::arg::DbusArg;
3 |
4 | use std::collections::HashMap;
5 | use std::sync::Mutex;
6 |
7 | use dbus::arg::Variant;
8 |
9 | use tracing::trace;
10 |
11 |
12 | #[derive(Debug)]
13 | pub struct Property {
14 | name: &'static str,
15 | value: Mutex,
16 | }
17 |
18 | impl Property {
19 | pub fn new(name: &'static str, value: T) -> Self {
20 | Self { name, value: Mutex::new(value) }
21 | }
22 |
23 | pub fn set(&self, conn: &C, value: T)
24 | where
25 | C: dbus::channel::Sender,
26 | T: DbusArg + PartialEq + std::fmt::Debug,
27 | {
28 | // update stored value and get variant
29 | let value = {
30 | let mut stored = self.value.lock().unwrap();
31 |
32 | // check for actual change
33 | if *stored == value {
34 | return;
35 | }
36 |
37 | trace!(target: "sdtxd::srvc", object=Service::PATH, interface=Service::INTERFACE,
38 | name=self.name, old=?*stored, new=?value, "changing property");
39 |
40 | *stored = value;
41 | stored.as_variant()
42 | };
43 |
44 | // signal property changed
45 | use dbus::arg::RefArg;
46 | use dbus::message::SignalArgs;
47 | use dbus::ffidisp::stdintf::org_freedesktop_dbus as dbffi;
48 | use dbffi::PropertiesPropertiesChanged as PropertiesChanged;
49 |
50 | let mut changed: HashMap>> = HashMap::new();
51 | changed.insert(self.name.into(), value);
52 |
53 | let changed = PropertiesChanged {
54 | interface_name: Service::INTERFACE.into(),
55 | changed_properties: changed,
56 | invalidated_properties: Vec::new(),
57 | };
58 |
59 | let msg = changed.to_emit_message(&Service::PATH.into());
60 |
61 | // send will only fail due to lack of memory
62 | conn.send(msg).unwrap();
63 | }
64 | }
65 |
66 | impl DbusArg for Property
67 | where
68 | T: DbusArg
69 | {
70 | type Arg = T::Arg;
71 |
72 | fn as_arg(&self) -> Self::Arg {
73 | self.value.lock().unwrap().as_arg()
74 | }
75 | }
76 |
77 | impl std::ops::Deref for Property {
78 | type Target = Mutex;
79 |
80 | fn deref(&self) -> &Self::Target {
81 | &self.value
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/surface-dtx-daemon/src/utils/mod.rs:
--------------------------------------------------------------------------------
1 | #[macro_use]
2 | mod tracing;
3 |
4 | pub mod scope;
5 | pub mod task;
6 | pub mod taskq;
7 |
--------------------------------------------------------------------------------
/surface-dtx-daemon/src/utils/scope.rs:
--------------------------------------------------------------------------------
1 |
2 | pub struct ScopeGuard {
3 | callback: Option,
4 | }
5 |
6 | impl Drop for ScopeGuard {
7 | fn drop(&mut self) {
8 | self.callback.take().unwrap()();
9 | }
10 | }
11 |
12 | pub fn guard(callback: F) -> ScopeGuard {
13 | ScopeGuard { callback: Some(callback) }
14 | }
15 |
--------------------------------------------------------------------------------
/surface-dtx-daemon/src/utils/task.rs:
--------------------------------------------------------------------------------
1 | use std::future::Future;
2 | use std::ops::{Deref, DerefMut};
3 | use std::pin::Pin;
4 | use std::task::{Context, Poll};
5 |
6 | use tokio::task::{JoinError, JoinHandle};
7 |
8 |
9 | /// Ensures that all tasks spawned by a future will be canceled when the future
10 | /// (i.e. created by an async fn) is dropped or when it completes.
11 | pub struct JoinGuard {
12 | inner: JoinHandle,
13 | }
14 |
15 | impl Drop for JoinGuard {
16 | fn drop(&mut self) {
17 | self.inner.abort();
18 | }
19 | }
20 |
21 | impl Deref for JoinGuard {
22 | type Target = JoinHandle;
23 |
24 | fn deref(&self) -> &Self::Target {
25 | &self.inner
26 | }
27 | }
28 |
29 | impl DerefMut for JoinGuard {
30 | fn deref_mut(&mut self) -> &mut Self::Target {
31 | &mut self.inner
32 | }
33 | }
34 |
35 | impl Future for JoinGuard {
36 | type Output = std::result::Result;
37 |
38 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll {
39 | std::pin::Pin::new(&mut self.inner).poll(cx)
40 | }
41 | }
42 |
43 |
44 | pub trait JoinHandleExt {
45 | fn guard(self) -> JoinGuard;
46 | }
47 |
48 | impl JoinHandleExt for JoinHandle {
49 | fn guard(self) -> JoinGuard {
50 | JoinGuard { inner: self }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/surface-dtx-daemon/src/utils/taskq.rs:
--------------------------------------------------------------------------------
1 | use std::future::Future;
2 | use std::pin::Pin;
3 |
4 | use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
5 | use tokio::sync::mpsc::error::SendError;
6 |
7 | use tracing::trace;
8 |
9 |
10 | pub type Task = Pin> + Send>>;
11 |
12 |
13 | #[derive(Debug)]
14 | pub struct TaskQueue {
15 | rx: UnboundedReceiver>,
16 | }
17 |
18 | impl TaskQueue {
19 | pub async fn run(&mut self) -> Result<(), E> {
20 | while let Some(task) = self.rx.recv().await {
21 | trace!(target: "sdtxd::tq", "running next task");
22 | let result = task.await;
23 | trace!(target: "sdtxd::tq", "task completed");
24 | result?;
25 | }
26 |
27 | Ok(())
28 | }
29 | }
30 |
31 |
32 | #[derive(Debug, Clone)]
33 | pub struct TaskSender {
34 | tx: UnboundedSender>,
35 | }
36 |
37 | impl TaskSender {
38 | pub fn submit(&self, task: T) -> Result<(), SendError>>
39 | where
40 | T: Future