gardesk/garcard / 65bfaf5

Browse files

seed sprint one scaffold

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
65bfaf5b76ed5b63c8c44726ce5810b17dd46da7
Tree
753e1d5

16 changed files

StatusFile+-
A .gitignore 3 0
A Cargo.lock 1744 0
A Cargo.toml 24 0
A README.md 25 0
A examples/config.toml 22 0
A garcard-ipc/Cargo.toml 10 0
A garcard-ipc/src/lib.rs 164 0
A garcard.service 14 0
A garcard/Cargo.toml 21 0
A garcard/src/agent.rs 185 0
A garcard/src/config.rs 209 0
A garcard/src/daemon.rs 278 0
A garcard/src/main.rs 56 0
A garcard/src/state.rs 135 0
A garcardctl/Cargo.toml 13 0
A garcardctl/src/main.rs 79 0
.gitignoreadded
@@ -0,0 +1,3 @@
1
+docs/
2
+target/
3
+target/
Cargo.lockadded
1744 lines changed — click to load
@@ -0,0 +1,1744 @@
1
+# This file is automatically @generated by Cargo.
2
+# It is not intended for manual editing.
3
+version = 4
4
+
5
+[[package]]
6
+name = "aho-corasick"
7
+version = "1.1.4"
8
+source = "registry+https://github.com/rust-lang/crates.io-index"
9
+checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
10
+dependencies = [
11
+ "memchr",
12
+]
13
+
14
+[[package]]
15
+name = "anstream"
16
+version = "0.6.21"
17
+source = "registry+https://github.com/rust-lang/crates.io-index"
18
+checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a"
19
+dependencies = [
20
+ "anstyle",
21
+ "anstyle-parse",
22
+ "anstyle-query",
23
+ "anstyle-wincon",
24
+ "colorchoice",
25
+ "is_terminal_polyfill",
26
+ "utf8parse",
27
+]
28
+
29
+[[package]]
30
+name = "anstyle"
31
+version = "1.0.13"
32
+source = "registry+https://github.com/rust-lang/crates.io-index"
33
+checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
34
+
35
+[[package]]
36
+name = "anstyle-parse"
37
+version = "0.2.7"
38
+source = "registry+https://github.com/rust-lang/crates.io-index"
39
+checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
40
+dependencies = [
41
+ "utf8parse",
42
+]
43
+
44
+[[package]]
45
+name = "anstyle-query"
46
+version = "1.1.5"
47
+source = "registry+https://github.com/rust-lang/crates.io-index"
48
+checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
49
+dependencies = [
50
+ "windows-sys 0.61.2",
51
+]
52
+
53
+[[package]]
54
+name = "anstyle-wincon"
55
+version = "3.0.11"
56
+source = "registry+https://github.com/rust-lang/crates.io-index"
57
+checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
58
+dependencies = [
59
+ "anstyle",
60
+ "once_cell_polyfill",
61
+ "windows-sys 0.61.2",
62
+]
63
+
64
+[[package]]
65
+name = "anyhow"
66
+version = "1.0.101"
67
+source = "registry+https://github.com/rust-lang/crates.io-index"
68
+checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea"
69
+
70
+[[package]]
71
+name = "async-broadcast"
72
+version = "0.7.2"
73
+source = "registry+https://github.com/rust-lang/crates.io-index"
74
+checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532"
75
+dependencies = [
76
+ "event-listener",
77
+ "event-listener-strategy",
78
+ "futures-core",
79
+ "pin-project-lite",
80
+]
81
+
82
+[[package]]
83
+name = "async-channel"
84
+version = "2.5.0"
85
+source = "registry+https://github.com/rust-lang/crates.io-index"
86
+checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2"
87
+dependencies = [
88
+ "concurrent-queue",
89
+ "event-listener-strategy",
90
+ "futures-core",
91
+ "pin-project-lite",
92
+]
93
+
94
+[[package]]
95
+name = "async-executor"
96
+version = "1.13.3"
97
+source = "registry+https://github.com/rust-lang/crates.io-index"
98
+checksum = "497c00e0fd83a72a79a39fcbd8e3e2f055d6f6c7e025f3b3d91f4f8e76527fb8"
99
+dependencies = [
100
+ "async-task",
101
+ "concurrent-queue",
102
+ "fastrand",
103
+ "futures-lite",
104
+ "pin-project-lite",
105
+ "slab",
106
+]
107
+
108
+[[package]]
109
+name = "async-fs"
110
+version = "2.2.0"
111
+source = "registry+https://github.com/rust-lang/crates.io-index"
112
+checksum = "8034a681df4aed8b8edbd7fbe472401ecf009251c8b40556b304567052e294c5"
113
+dependencies = [
114
+ "async-lock",
115
+ "blocking",
116
+ "futures-lite",
117
+]
118
+
119
+[[package]]
120
+name = "async-io"
121
+version = "2.6.0"
122
+source = "registry+https://github.com/rust-lang/crates.io-index"
123
+checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc"
124
+dependencies = [
125
+ "autocfg",
126
+ "cfg-if",
127
+ "concurrent-queue",
128
+ "futures-io",
129
+ "futures-lite",
130
+ "parking",
131
+ "polling",
132
+ "rustix",
133
+ "slab",
134
+ "windows-sys 0.61.2",
135
+]
136
+
137
+[[package]]
138
+name = "async-lock"
139
+version = "3.4.2"
140
+source = "registry+https://github.com/rust-lang/crates.io-index"
141
+checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311"
142
+dependencies = [
143
+ "event-listener",
144
+ "event-listener-strategy",
145
+ "pin-project-lite",
146
+]
147
+
148
+[[package]]
149
+name = "async-process"
150
+version = "2.5.0"
151
+source = "registry+https://github.com/rust-lang/crates.io-index"
152
+checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75"
153
+dependencies = [
154
+ "async-channel",
155
+ "async-io",
156
+ "async-lock",
157
+ "async-signal",
158
+ "async-task",
159
+ "blocking",
160
+ "cfg-if",
161
+ "event-listener",
162
+ "futures-lite",
163
+ "rustix",
164
+]
165
+
166
+[[package]]
167
+name = "async-recursion"
168
+version = "1.1.1"
169
+source = "registry+https://github.com/rust-lang/crates.io-index"
170
+checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
171
+dependencies = [
172
+ "proc-macro2",
173
+ "quote",
174
+ "syn",
175
+]
176
+
177
+[[package]]
178
+name = "async-signal"
179
+version = "0.2.13"
180
+source = "registry+https://github.com/rust-lang/crates.io-index"
181
+checksum = "43c070bbf59cd3570b6b2dd54cd772527c7c3620fce8be898406dd3ed6adc64c"
182
+dependencies = [
183
+ "async-io",
184
+ "async-lock",
185
+ "atomic-waker",
186
+ "cfg-if",
187
+ "futures-core",
188
+ "futures-io",
189
+ "rustix",
190
+ "signal-hook-registry",
191
+ "slab",
192
+ "windows-sys 0.61.2",
193
+]
194
+
195
+[[package]]
196
+name = "async-task"
197
+version = "4.7.1"
198
+source = "registry+https://github.com/rust-lang/crates.io-index"
199
+checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
200
+
201
+[[package]]
202
+name = "async-trait"
203
+version = "0.1.89"
204
+source = "registry+https://github.com/rust-lang/crates.io-index"
205
+checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb"
206
+dependencies = [
207
+ "proc-macro2",
208
+ "quote",
209
+ "syn",
210
+]
211
+
212
+[[package]]
213
+name = "atomic-waker"
214
+version = "1.1.2"
215
+source = "registry+https://github.com/rust-lang/crates.io-index"
216
+checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
217
+
218
+[[package]]
219
+name = "autocfg"
220
+version = "1.5.0"
221
+source = "registry+https://github.com/rust-lang/crates.io-index"
222
+checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
223
+
224
+[[package]]
225
+name = "bitflags"
226
+version = "2.11.0"
227
+source = "registry+https://github.com/rust-lang/crates.io-index"
228
+checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
229
+
230
+[[package]]
231
+name = "block-buffer"
232
+version = "0.10.4"
233
+source = "registry+https://github.com/rust-lang/crates.io-index"
234
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
235
+dependencies = [
236
+ "generic-array",
237
+]
238
+
239
+[[package]]
240
+name = "blocking"
241
+version = "1.6.2"
242
+source = "registry+https://github.com/rust-lang/crates.io-index"
243
+checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21"
244
+dependencies = [
245
+ "async-channel",
246
+ "async-task",
247
+ "futures-io",
248
+ "futures-lite",
249
+ "piper",
250
+]
251
+
252
+[[package]]
253
+name = "bytes"
254
+version = "1.11.1"
255
+source = "registry+https://github.com/rust-lang/crates.io-index"
256
+checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
257
+
258
+[[package]]
259
+name = "cfg-if"
260
+version = "1.0.4"
261
+source = "registry+https://github.com/rust-lang/crates.io-index"
262
+checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
263
+
264
+[[package]]
265
+name = "cfg_aliases"
266
+version = "0.2.1"
267
+source = "registry+https://github.com/rust-lang/crates.io-index"
268
+checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
269
+
270
+[[package]]
271
+name = "clap"
272
+version = "4.5.59"
273
+source = "registry+https://github.com/rust-lang/crates.io-index"
274
+checksum = "c5caf74d17c3aec5495110c34cc3f78644bfa89af6c8993ed4de2790e49b6499"
275
+dependencies = [
276
+ "clap_builder",
277
+ "clap_derive",
278
+]
279
+
280
+[[package]]
281
+name = "clap_builder"
282
+version = "4.5.59"
283
+source = "registry+https://github.com/rust-lang/crates.io-index"
284
+checksum = "370daa45065b80218950227371916a1633217ae42b2715b2287b606dcd618e24"
285
+dependencies = [
286
+ "anstream",
287
+ "anstyle",
288
+ "clap_lex",
289
+ "strsim",
290
+]
291
+
292
+[[package]]
293
+name = "clap_derive"
294
+version = "4.5.55"
295
+source = "registry+https://github.com/rust-lang/crates.io-index"
296
+checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5"
297
+dependencies = [
298
+ "heck",
299
+ "proc-macro2",
300
+ "quote",
301
+ "syn",
302
+]
303
+
304
+[[package]]
305
+name = "clap_lex"
306
+version = "1.0.0"
307
+source = "registry+https://github.com/rust-lang/crates.io-index"
308
+checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831"
309
+
310
+[[package]]
311
+name = "colorchoice"
312
+version = "1.0.4"
313
+source = "registry+https://github.com/rust-lang/crates.io-index"
314
+checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
315
+
316
+[[package]]
317
+name = "concurrent-queue"
318
+version = "2.5.0"
319
+source = "registry+https://github.com/rust-lang/crates.io-index"
320
+checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973"
321
+dependencies = [
322
+ "crossbeam-utils",
323
+]
324
+
325
+[[package]]
326
+name = "cpufeatures"
327
+version = "0.2.17"
328
+source = "registry+https://github.com/rust-lang/crates.io-index"
329
+checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
330
+dependencies = [
331
+ "libc",
332
+]
333
+
334
+[[package]]
335
+name = "crossbeam-utils"
336
+version = "0.8.21"
337
+source = "registry+https://github.com/rust-lang/crates.io-index"
338
+checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
339
+
340
+[[package]]
341
+name = "crypto-common"
342
+version = "0.1.7"
343
+source = "registry+https://github.com/rust-lang/crates.io-index"
344
+checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
345
+dependencies = [
346
+ "generic-array",
347
+ "typenum",
348
+]
349
+
350
+[[package]]
351
+name = "digest"
352
+version = "0.10.7"
353
+source = "registry+https://github.com/rust-lang/crates.io-index"
354
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
355
+dependencies = [
356
+ "block-buffer",
357
+ "crypto-common",
358
+]
359
+
360
+[[package]]
361
+name = "dirs"
362
+version = "5.0.1"
363
+source = "registry+https://github.com/rust-lang/crates.io-index"
364
+checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
365
+dependencies = [
366
+ "dirs-sys",
367
+]
368
+
369
+[[package]]
370
+name = "dirs-sys"
371
+version = "0.4.1"
372
+source = "registry+https://github.com/rust-lang/crates.io-index"
373
+checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
374
+dependencies = [
375
+ "libc",
376
+ "option-ext",
377
+ "redox_users",
378
+ "windows-sys 0.48.0",
379
+]
380
+
381
+[[package]]
382
+name = "endi"
383
+version = "1.1.1"
384
+source = "registry+https://github.com/rust-lang/crates.io-index"
385
+checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099"
386
+
387
+[[package]]
388
+name = "enumflags2"
389
+version = "0.7.12"
390
+source = "registry+https://github.com/rust-lang/crates.io-index"
391
+checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef"
392
+dependencies = [
393
+ "enumflags2_derive",
394
+ "serde",
395
+]
396
+
397
+[[package]]
398
+name = "enumflags2_derive"
399
+version = "0.7.12"
400
+source = "registry+https://github.com/rust-lang/crates.io-index"
401
+checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827"
402
+dependencies = [
403
+ "proc-macro2",
404
+ "quote",
405
+ "syn",
406
+]
407
+
408
+[[package]]
409
+name = "equivalent"
410
+version = "1.0.2"
411
+source = "registry+https://github.com/rust-lang/crates.io-index"
412
+checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
413
+
414
+[[package]]
415
+name = "errno"
416
+version = "0.3.14"
417
+source = "registry+https://github.com/rust-lang/crates.io-index"
418
+checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
419
+dependencies = [
420
+ "libc",
421
+ "windows-sys 0.61.2",
422
+]
423
+
424
+[[package]]
425
+name = "event-listener"
426
+version = "5.4.1"
427
+source = "registry+https://github.com/rust-lang/crates.io-index"
428
+checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab"
429
+dependencies = [
430
+ "concurrent-queue",
431
+ "parking",
432
+ "pin-project-lite",
433
+]
434
+
435
+[[package]]
436
+name = "event-listener-strategy"
437
+version = "0.5.4"
438
+source = "registry+https://github.com/rust-lang/crates.io-index"
439
+checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93"
440
+dependencies = [
441
+ "event-listener",
442
+ "pin-project-lite",
443
+]
444
+
445
+[[package]]
446
+name = "fastrand"
447
+version = "2.3.0"
448
+source = "registry+https://github.com/rust-lang/crates.io-index"
449
+checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
450
+
451
+[[package]]
452
+name = "futures-core"
453
+version = "0.3.32"
454
+source = "registry+https://github.com/rust-lang/crates.io-index"
455
+checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
456
+
457
+[[package]]
458
+name = "futures-io"
459
+version = "0.3.32"
460
+source = "registry+https://github.com/rust-lang/crates.io-index"
461
+checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718"
462
+
463
+[[package]]
464
+name = "futures-lite"
465
+version = "2.6.1"
466
+source = "registry+https://github.com/rust-lang/crates.io-index"
467
+checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad"
468
+dependencies = [
469
+ "fastrand",
470
+ "futures-core",
471
+ "futures-io",
472
+ "parking",
473
+ "pin-project-lite",
474
+]
475
+
476
+[[package]]
477
+name = "futures-sink"
478
+version = "0.3.31"
479
+source = "registry+https://github.com/rust-lang/crates.io-index"
480
+checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
481
+
482
+[[package]]
483
+name = "futures-task"
484
+version = "0.3.32"
485
+source = "registry+https://github.com/rust-lang/crates.io-index"
486
+checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
487
+
488
+[[package]]
489
+name = "futures-util"
490
+version = "0.3.31"
491
+source = "registry+https://github.com/rust-lang/crates.io-index"
492
+checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
493
+dependencies = [
494
+ "futures-core",
495
+ "futures-io",
496
+ "futures-sink",
497
+ "futures-task",
498
+ "memchr",
499
+ "pin-project-lite",
500
+ "pin-utils",
501
+ "slab",
502
+]
503
+
504
+[[package]]
505
+name = "garcard"
506
+version = "0.1.0"
507
+dependencies = [
508
+ "anyhow",
509
+ "clap",
510
+ "dirs",
511
+ "garcard-ipc",
512
+ "nix",
513
+ "serde",
514
+ "serde_json",
515
+ "tokio",
516
+ "toml",
517
+ "tracing",
518
+ "tracing-subscriber",
519
+ "zbus",
520
+]
521
+
522
+[[package]]
523
+name = "garcard-ipc"
524
+version = "0.1.0"
525
+dependencies = [
526
+ "serde",
527
+ "serde_json",
528
+]
529
+
530
+[[package]]
531
+name = "garcardctl"
532
+version = "0.1.0"
533
+dependencies = [
534
+ "anyhow",
535
+ "clap",
536
+ "garcard-ipc",
537
+ "serde_json",
538
+]
539
+
540
+[[package]]
541
+name = "generic-array"
542
+version = "0.14.7"
543
+source = "registry+https://github.com/rust-lang/crates.io-index"
544
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
545
+dependencies = [
546
+ "typenum",
547
+ "version_check",
548
+]
549
+
550
+[[package]]
551
+name = "getrandom"
552
+version = "0.2.17"
553
+source = "registry+https://github.com/rust-lang/crates.io-index"
554
+checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
555
+dependencies = [
556
+ "cfg-if",
557
+ "libc",
558
+ "wasi",
559
+]
560
+
561
+[[package]]
562
+name = "getrandom"
563
+version = "0.3.4"
564
+source = "registry+https://github.com/rust-lang/crates.io-index"
565
+checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
566
+dependencies = [
567
+ "cfg-if",
568
+ "libc",
569
+ "r-efi",
570
+ "wasip2",
571
+]
572
+
573
+[[package]]
574
+name = "hashbrown"
575
+version = "0.16.1"
576
+source = "registry+https://github.com/rust-lang/crates.io-index"
577
+checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
578
+
579
+[[package]]
580
+name = "heck"
581
+version = "0.5.0"
582
+source = "registry+https://github.com/rust-lang/crates.io-index"
583
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
584
+
585
+[[package]]
586
+name = "hermit-abi"
587
+version = "0.5.2"
588
+source = "registry+https://github.com/rust-lang/crates.io-index"
589
+checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
590
+
591
+[[package]]
592
+name = "hex"
593
+version = "0.4.3"
594
+source = "registry+https://github.com/rust-lang/crates.io-index"
595
+checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
596
+
597
+[[package]]
598
+name = "indexmap"
599
+version = "2.13.0"
600
+source = "registry+https://github.com/rust-lang/crates.io-index"
601
+checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
602
+dependencies = [
603
+ "equivalent",
604
+ "hashbrown",
605
+]
606
+
607
+[[package]]
608
+name = "is_terminal_polyfill"
609
+version = "1.70.2"
610
+source = "registry+https://github.com/rust-lang/crates.io-index"
611
+checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
612
+
613
+[[package]]
614
+name = "itoa"
615
+version = "1.0.17"
616
+source = "registry+https://github.com/rust-lang/crates.io-index"
617
+checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
618
+
619
+[[package]]
620
+name = "lazy_static"
621
+version = "1.5.0"
622
+source = "registry+https://github.com/rust-lang/crates.io-index"
623
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
624
+
625
+[[package]]
626
+name = "libc"
627
+version = "0.2.182"
628
+source = "registry+https://github.com/rust-lang/crates.io-index"
629
+checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112"
630
+
631
+[[package]]
632
+name = "libredox"
633
+version = "0.1.12"
634
+source = "registry+https://github.com/rust-lang/crates.io-index"
635
+checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616"
636
+dependencies = [
637
+ "bitflags",
638
+ "libc",
639
+]
640
+
641
+[[package]]
642
+name = "linux-raw-sys"
643
+version = "0.11.0"
644
+source = "registry+https://github.com/rust-lang/crates.io-index"
645
+checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
646
+
647
+[[package]]
648
+name = "lock_api"
649
+version = "0.4.14"
650
+source = "registry+https://github.com/rust-lang/crates.io-index"
651
+checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
652
+dependencies = [
653
+ "scopeguard",
654
+]
655
+
656
+[[package]]
657
+name = "log"
658
+version = "0.4.29"
659
+source = "registry+https://github.com/rust-lang/crates.io-index"
660
+checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
661
+
662
+[[package]]
663
+name = "matchers"
664
+version = "0.2.0"
665
+source = "registry+https://github.com/rust-lang/crates.io-index"
666
+checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
667
+dependencies = [
668
+ "regex-automata",
669
+]
670
+
671
+[[package]]
672
+name = "memchr"
673
+version = "2.8.0"
674
+source = "registry+https://github.com/rust-lang/crates.io-index"
675
+checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
676
+
677
+[[package]]
678
+name = "memoffset"
679
+version = "0.9.1"
680
+source = "registry+https://github.com/rust-lang/crates.io-index"
681
+checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
682
+dependencies = [
683
+ "autocfg",
684
+]
685
+
686
+[[package]]
687
+name = "mio"
688
+version = "1.1.1"
689
+source = "registry+https://github.com/rust-lang/crates.io-index"
690
+checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc"
691
+dependencies = [
692
+ "libc",
693
+ "wasi",
694
+ "windows-sys 0.61.2",
695
+]
696
+
697
+[[package]]
698
+name = "nix"
699
+version = "0.29.0"
700
+source = "registry+https://github.com/rust-lang/crates.io-index"
701
+checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
702
+dependencies = [
703
+ "bitflags",
704
+ "cfg-if",
705
+ "cfg_aliases",
706
+ "libc",
707
+ "memoffset",
708
+]
709
+
710
+[[package]]
711
+name = "nu-ansi-term"
712
+version = "0.50.3"
713
+source = "registry+https://github.com/rust-lang/crates.io-index"
714
+checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
715
+dependencies = [
716
+ "windows-sys 0.61.2",
717
+]
718
+
719
+[[package]]
720
+name = "once_cell"
721
+version = "1.21.3"
722
+source = "registry+https://github.com/rust-lang/crates.io-index"
723
+checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
724
+
725
+[[package]]
726
+name = "once_cell_polyfill"
727
+version = "1.70.2"
728
+source = "registry+https://github.com/rust-lang/crates.io-index"
729
+checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
730
+
731
+[[package]]
732
+name = "option-ext"
733
+version = "0.2.0"
734
+source = "registry+https://github.com/rust-lang/crates.io-index"
735
+checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
736
+
737
+[[package]]
738
+name = "ordered-stream"
739
+version = "0.2.0"
740
+source = "registry+https://github.com/rust-lang/crates.io-index"
741
+checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50"
742
+dependencies = [
743
+ "futures-core",
744
+ "pin-project-lite",
745
+]
746
+
747
+[[package]]
748
+name = "parking"
749
+version = "2.2.1"
750
+source = "registry+https://github.com/rust-lang/crates.io-index"
751
+checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba"
752
+
753
+[[package]]
754
+name = "parking_lot"
755
+version = "0.12.5"
756
+source = "registry+https://github.com/rust-lang/crates.io-index"
757
+checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
758
+dependencies = [
759
+ "lock_api",
760
+ "parking_lot_core",
761
+]
762
+
763
+[[package]]
764
+name = "parking_lot_core"
765
+version = "0.9.12"
766
+source = "registry+https://github.com/rust-lang/crates.io-index"
767
+checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
768
+dependencies = [
769
+ "cfg-if",
770
+ "libc",
771
+ "redox_syscall",
772
+ "smallvec",
773
+ "windows-link",
774
+]
775
+
776
+[[package]]
777
+name = "pin-project-lite"
778
+version = "0.2.16"
779
+source = "registry+https://github.com/rust-lang/crates.io-index"
780
+checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
781
+
782
+[[package]]
783
+name = "pin-utils"
784
+version = "0.1.0"
785
+source = "registry+https://github.com/rust-lang/crates.io-index"
786
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
787
+
788
+[[package]]
789
+name = "piper"
790
+version = "0.2.4"
791
+source = "registry+https://github.com/rust-lang/crates.io-index"
792
+checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066"
793
+dependencies = [
794
+ "atomic-waker",
795
+ "fastrand",
796
+ "futures-io",
797
+]
798
+
799
+[[package]]
800
+name = "polling"
801
+version = "3.11.0"
802
+source = "registry+https://github.com/rust-lang/crates.io-index"
803
+checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218"
804
+dependencies = [
805
+ "cfg-if",
806
+ "concurrent-queue",
807
+ "hermit-abi",
808
+ "pin-project-lite",
809
+ "rustix",
810
+ "windows-sys 0.61.2",
811
+]
812
+
813
+[[package]]
814
+name = "ppv-lite86"
815
+version = "0.2.21"
816
+source = "registry+https://github.com/rust-lang/crates.io-index"
817
+checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
818
+dependencies = [
819
+ "zerocopy",
820
+]
821
+
822
+[[package]]
823
+name = "proc-macro-crate"
824
+version = "3.4.0"
825
+source = "registry+https://github.com/rust-lang/crates.io-index"
826
+checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983"
827
+dependencies = [
828
+ "toml_edit 0.23.10+spec-1.0.0",
829
+]
830
+
831
+[[package]]
832
+name = "proc-macro2"
833
+version = "1.0.106"
834
+source = "registry+https://github.com/rust-lang/crates.io-index"
835
+checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
836
+dependencies = [
837
+ "unicode-ident",
838
+]
839
+
840
+[[package]]
841
+name = "quote"
842
+version = "1.0.44"
843
+source = "registry+https://github.com/rust-lang/crates.io-index"
844
+checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
845
+dependencies = [
846
+ "proc-macro2",
847
+]
848
+
849
+[[package]]
850
+name = "r-efi"
851
+version = "5.3.0"
852
+source = "registry+https://github.com/rust-lang/crates.io-index"
853
+checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
854
+
855
+[[package]]
856
+name = "rand"
857
+version = "0.8.5"
858
+source = "registry+https://github.com/rust-lang/crates.io-index"
859
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
860
+dependencies = [
861
+ "libc",
862
+ "rand_chacha",
863
+ "rand_core",
864
+]
865
+
866
+[[package]]
867
+name = "rand_chacha"
868
+version = "0.3.1"
869
+source = "registry+https://github.com/rust-lang/crates.io-index"
870
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
871
+dependencies = [
872
+ "ppv-lite86",
873
+ "rand_core",
874
+]
875
+
876
+[[package]]
877
+name = "rand_core"
878
+version = "0.6.4"
879
+source = "registry+https://github.com/rust-lang/crates.io-index"
880
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
881
+dependencies = [
882
+ "getrandom 0.2.17",
883
+]
884
+
885
+[[package]]
886
+name = "redox_syscall"
887
+version = "0.5.18"
888
+source = "registry+https://github.com/rust-lang/crates.io-index"
889
+checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
890
+dependencies = [
891
+ "bitflags",
892
+]
893
+
894
+[[package]]
895
+name = "redox_users"
896
+version = "0.4.6"
897
+source = "registry+https://github.com/rust-lang/crates.io-index"
898
+checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
899
+dependencies = [
900
+ "getrandom 0.2.17",
901
+ "libredox",
902
+ "thiserror",
903
+]
904
+
905
+[[package]]
906
+name = "regex-automata"
907
+version = "0.4.14"
908
+source = "registry+https://github.com/rust-lang/crates.io-index"
909
+checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
910
+dependencies = [
911
+ "aho-corasick",
912
+ "memchr",
913
+ "regex-syntax",
914
+]
915
+
916
+[[package]]
917
+name = "regex-syntax"
918
+version = "0.8.9"
919
+source = "registry+https://github.com/rust-lang/crates.io-index"
920
+checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c"
921
+
922
+[[package]]
923
+name = "rustix"
924
+version = "1.1.3"
925
+source = "registry+https://github.com/rust-lang/crates.io-index"
926
+checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34"
927
+dependencies = [
928
+ "bitflags",
929
+ "errno",
930
+ "libc",
931
+ "linux-raw-sys",
932
+ "windows-sys 0.61.2",
933
+]
934
+
935
+[[package]]
936
+name = "scopeguard"
937
+version = "1.2.0"
938
+source = "registry+https://github.com/rust-lang/crates.io-index"
939
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
940
+
941
+[[package]]
942
+name = "serde"
943
+version = "1.0.228"
944
+source = "registry+https://github.com/rust-lang/crates.io-index"
945
+checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
946
+dependencies = [
947
+ "serde_core",
948
+ "serde_derive",
949
+]
950
+
951
+[[package]]
952
+name = "serde_core"
953
+version = "1.0.228"
954
+source = "registry+https://github.com/rust-lang/crates.io-index"
955
+checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
956
+dependencies = [
957
+ "serde_derive",
958
+]
959
+
960
+[[package]]
961
+name = "serde_derive"
962
+version = "1.0.228"
963
+source = "registry+https://github.com/rust-lang/crates.io-index"
964
+checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
965
+dependencies = [
966
+ "proc-macro2",
967
+ "quote",
968
+ "syn",
969
+]
970
+
971
+[[package]]
972
+name = "serde_json"
973
+version = "1.0.149"
974
+source = "registry+https://github.com/rust-lang/crates.io-index"
975
+checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
976
+dependencies = [
977
+ "itoa",
978
+ "memchr",
979
+ "serde",
980
+ "serde_core",
981
+ "zmij",
982
+]
983
+
984
+[[package]]
985
+name = "serde_repr"
986
+version = "0.1.20"
987
+source = "registry+https://github.com/rust-lang/crates.io-index"
988
+checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c"
989
+dependencies = [
990
+ "proc-macro2",
991
+ "quote",
992
+ "syn",
993
+]
994
+
995
+[[package]]
996
+name = "serde_spanned"
997
+version = "0.6.9"
998
+source = "registry+https://github.com/rust-lang/crates.io-index"
999
+checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3"
1000
+dependencies = [
1001
+ "serde",
1002
+]
1003
+
1004
+[[package]]
1005
+name = "sha1"
1006
+version = "0.10.6"
1007
+source = "registry+https://github.com/rust-lang/crates.io-index"
1008
+checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
1009
+dependencies = [
1010
+ "cfg-if",
1011
+ "cpufeatures",
1012
+ "digest",
1013
+]
1014
+
1015
+[[package]]
1016
+name = "sharded-slab"
1017
+version = "0.1.7"
1018
+source = "registry+https://github.com/rust-lang/crates.io-index"
1019
+checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
1020
+dependencies = [
1021
+ "lazy_static",
1022
+]
1023
+
1024
+[[package]]
1025
+name = "signal-hook-registry"
1026
+version = "1.4.8"
1027
+source = "registry+https://github.com/rust-lang/crates.io-index"
1028
+checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
1029
+dependencies = [
1030
+ "errno",
1031
+ "libc",
1032
+]
1033
+
1034
+[[package]]
1035
+name = "slab"
1036
+version = "0.4.12"
1037
+source = "registry+https://github.com/rust-lang/crates.io-index"
1038
+checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
1039
+
1040
+[[package]]
1041
+name = "smallvec"
1042
+version = "1.15.1"
1043
+source = "registry+https://github.com/rust-lang/crates.io-index"
1044
+checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
1045
+
1046
+[[package]]
1047
+name = "socket2"
1048
+version = "0.6.2"
1049
+source = "registry+https://github.com/rust-lang/crates.io-index"
1050
+checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0"
1051
+dependencies = [
1052
+ "libc",
1053
+ "windows-sys 0.60.2",
1054
+]
1055
+
1056
+[[package]]
1057
+name = "static_assertions"
1058
+version = "1.1.0"
1059
+source = "registry+https://github.com/rust-lang/crates.io-index"
1060
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
1061
+
1062
+[[package]]
1063
+name = "strsim"
1064
+version = "0.11.1"
1065
+source = "registry+https://github.com/rust-lang/crates.io-index"
1066
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
1067
+
1068
+[[package]]
1069
+name = "syn"
1070
+version = "2.0.116"
1071
+source = "registry+https://github.com/rust-lang/crates.io-index"
1072
+checksum = "3df424c70518695237746f84cede799c9c58fcb37450d7b23716568cc8bc69cb"
1073
+dependencies = [
1074
+ "proc-macro2",
1075
+ "quote",
1076
+ "unicode-ident",
1077
+]
1078
+
1079
+[[package]]
1080
+name = "tempfile"
1081
+version = "3.24.0"
1082
+source = "registry+https://github.com/rust-lang/crates.io-index"
1083
+checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c"
1084
+dependencies = [
1085
+ "fastrand",
1086
+ "getrandom 0.3.4",
1087
+ "once_cell",
1088
+ "rustix",
1089
+ "windows-sys 0.61.2",
1090
+]
1091
+
1092
+[[package]]
1093
+name = "thiserror"
1094
+version = "1.0.69"
1095
+source = "registry+https://github.com/rust-lang/crates.io-index"
1096
+checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
1097
+dependencies = [
1098
+ "thiserror-impl",
1099
+]
1100
+
1101
+[[package]]
1102
+name = "thiserror-impl"
1103
+version = "1.0.69"
1104
+source = "registry+https://github.com/rust-lang/crates.io-index"
1105
+checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
1106
+dependencies = [
1107
+ "proc-macro2",
1108
+ "quote",
1109
+ "syn",
1110
+]
1111
+
1112
+[[package]]
1113
+name = "thread_local"
1114
+version = "1.1.9"
1115
+source = "registry+https://github.com/rust-lang/crates.io-index"
1116
+checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
1117
+dependencies = [
1118
+ "cfg-if",
1119
+]
1120
+
1121
+[[package]]
1122
+name = "tokio"
1123
+version = "1.49.0"
1124
+source = "registry+https://github.com/rust-lang/crates.io-index"
1125
+checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86"
1126
+dependencies = [
1127
+ "bytes",
1128
+ "libc",
1129
+ "mio",
1130
+ "parking_lot",
1131
+ "pin-project-lite",
1132
+ "signal-hook-registry",
1133
+ "socket2",
1134
+ "tokio-macros",
1135
+ "windows-sys 0.61.2",
1136
+]
1137
+
1138
+[[package]]
1139
+name = "tokio-macros"
1140
+version = "2.6.0"
1141
+source = "registry+https://github.com/rust-lang/crates.io-index"
1142
+checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5"
1143
+dependencies = [
1144
+ "proc-macro2",
1145
+ "quote",
1146
+ "syn",
1147
+]
1148
+
1149
+[[package]]
1150
+name = "toml"
1151
+version = "0.8.23"
1152
+source = "registry+https://github.com/rust-lang/crates.io-index"
1153
+checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362"
1154
+dependencies = [
1155
+ "serde",
1156
+ "serde_spanned",
1157
+ "toml_datetime 0.6.11",
1158
+ "toml_edit 0.22.27",
1159
+]
1160
+
1161
+[[package]]
1162
+name = "toml_datetime"
1163
+version = "0.6.11"
1164
+source = "registry+https://github.com/rust-lang/crates.io-index"
1165
+checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
1166
+dependencies = [
1167
+ "serde",
1168
+]
1169
+
1170
+[[package]]
1171
+name = "toml_datetime"
1172
+version = "0.7.5+spec-1.1.0"
1173
+source = "registry+https://github.com/rust-lang/crates.io-index"
1174
+checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347"
1175
+dependencies = [
1176
+ "serde_core",
1177
+]
1178
+
1179
+[[package]]
1180
+name = "toml_edit"
1181
+version = "0.22.27"
1182
+source = "registry+https://github.com/rust-lang/crates.io-index"
1183
+checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
1184
+dependencies = [
1185
+ "indexmap",
1186
+ "serde",
1187
+ "serde_spanned",
1188
+ "toml_datetime 0.6.11",
1189
+ "toml_write",
1190
+ "winnow",
1191
+]
1192
+
1193
+[[package]]
1194
+name = "toml_edit"
1195
+version = "0.23.10+spec-1.0.0"
1196
+source = "registry+https://github.com/rust-lang/crates.io-index"
1197
+checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269"
1198
+dependencies = [
1199
+ "indexmap",
1200
+ "toml_datetime 0.7.5+spec-1.1.0",
1201
+ "toml_parser",
1202
+ "winnow",
1203
+]
1204
+
1205
+[[package]]
1206
+name = "toml_parser"
1207
+version = "1.0.9+spec-1.1.0"
1208
+source = "registry+https://github.com/rust-lang/crates.io-index"
1209
+checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4"
1210
+dependencies = [
1211
+ "winnow",
1212
+]
1213
+
1214
+[[package]]
1215
+name = "toml_write"
1216
+version = "0.1.2"
1217
+source = "registry+https://github.com/rust-lang/crates.io-index"
1218
+checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
1219
+
1220
+[[package]]
1221
+name = "tracing"
1222
+version = "0.1.44"
1223
+source = "registry+https://github.com/rust-lang/crates.io-index"
1224
+checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
1225
+dependencies = [
1226
+ "pin-project-lite",
1227
+ "tracing-attributes",
1228
+ "tracing-core",
1229
+]
1230
+
1231
+[[package]]
1232
+name = "tracing-attributes"
1233
+version = "0.1.31"
1234
+source = "registry+https://github.com/rust-lang/crates.io-index"
1235
+checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
1236
+dependencies = [
1237
+ "proc-macro2",
1238
+ "quote",
1239
+ "syn",
1240
+]
1241
+
1242
+[[package]]
1243
+name = "tracing-core"
1244
+version = "0.1.36"
1245
+source = "registry+https://github.com/rust-lang/crates.io-index"
1246
+checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
1247
+dependencies = [
1248
+ "once_cell",
1249
+ "valuable",
1250
+]
1251
+
1252
+[[package]]
1253
+name = "tracing-log"
1254
+version = "0.2.0"
1255
+source = "registry+https://github.com/rust-lang/crates.io-index"
1256
+checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
1257
+dependencies = [
1258
+ "log",
1259
+ "once_cell",
1260
+ "tracing-core",
1261
+]
1262
+
1263
+[[package]]
1264
+name = "tracing-subscriber"
1265
+version = "0.3.22"
1266
+source = "registry+https://github.com/rust-lang/crates.io-index"
1267
+checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e"
1268
+dependencies = [
1269
+ "matchers",
1270
+ "nu-ansi-term",
1271
+ "once_cell",
1272
+ "regex-automata",
1273
+ "sharded-slab",
1274
+ "smallvec",
1275
+ "thread_local",
1276
+ "tracing",
1277
+ "tracing-core",
1278
+ "tracing-log",
1279
+]
1280
+
1281
+[[package]]
1282
+name = "typenum"
1283
+version = "1.19.0"
1284
+source = "registry+https://github.com/rust-lang/crates.io-index"
1285
+checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
1286
+
1287
+[[package]]
1288
+name = "uds_windows"
1289
+version = "1.1.0"
1290
+source = "registry+https://github.com/rust-lang/crates.io-index"
1291
+checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9"
1292
+dependencies = [
1293
+ "memoffset",
1294
+ "tempfile",
1295
+ "winapi",
1296
+]
1297
+
1298
+[[package]]
1299
+name = "unicode-ident"
1300
+version = "1.0.24"
1301
+source = "registry+https://github.com/rust-lang/crates.io-index"
1302
+checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
1303
+
1304
+[[package]]
1305
+name = "utf8parse"
1306
+version = "0.2.2"
1307
+source = "registry+https://github.com/rust-lang/crates.io-index"
1308
+checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
1309
+
1310
+[[package]]
1311
+name = "valuable"
1312
+version = "0.1.1"
1313
+source = "registry+https://github.com/rust-lang/crates.io-index"
1314
+checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
1315
+
1316
+[[package]]
1317
+name = "version_check"
1318
+version = "0.9.5"
1319
+source = "registry+https://github.com/rust-lang/crates.io-index"
1320
+checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
1321
+
1322
+[[package]]
1323
+name = "wasi"
1324
+version = "0.11.1+wasi-snapshot-preview1"
1325
+source = "registry+https://github.com/rust-lang/crates.io-index"
1326
+checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
1327
+
1328
+[[package]]
1329
+name = "wasip2"
1330
+version = "1.0.1+wasi-0.2.4"
1331
+source = "registry+https://github.com/rust-lang/crates.io-index"
1332
+checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
1333
+dependencies = [
1334
+ "wit-bindgen",
1335
+]
1336
+
1337
+[[package]]
1338
+name = "winapi"
1339
+version = "0.3.9"
1340
+source = "registry+https://github.com/rust-lang/crates.io-index"
1341
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
1342
+dependencies = [
1343
+ "winapi-i686-pc-windows-gnu",
1344
+ "winapi-x86_64-pc-windows-gnu",
1345
+]
1346
+
1347
+[[package]]
1348
+name = "winapi-i686-pc-windows-gnu"
1349
+version = "0.4.0"
1350
+source = "registry+https://github.com/rust-lang/crates.io-index"
1351
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
1352
+
1353
+[[package]]
1354
+name = "winapi-x86_64-pc-windows-gnu"
1355
+version = "0.4.0"
1356
+source = "registry+https://github.com/rust-lang/crates.io-index"
1357
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
1358
+
1359
+[[package]]
1360
+name = "windows-link"
1361
+version = "0.2.1"
1362
+source = "registry+https://github.com/rust-lang/crates.io-index"
1363
+checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
1364
+
1365
+[[package]]
1366
+name = "windows-sys"
1367
+version = "0.48.0"
1368
+source = "registry+https://github.com/rust-lang/crates.io-index"
1369
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
1370
+dependencies = [
1371
+ "windows-targets 0.48.5",
1372
+]
1373
+
1374
+[[package]]
1375
+name = "windows-sys"
1376
+version = "0.52.0"
1377
+source = "registry+https://github.com/rust-lang/crates.io-index"
1378
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
1379
+dependencies = [
1380
+ "windows-targets 0.52.6",
1381
+]
1382
+
1383
+[[package]]
1384
+name = "windows-sys"
1385
+version = "0.59.0"
1386
+source = "registry+https://github.com/rust-lang/crates.io-index"
1387
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
1388
+dependencies = [
1389
+ "windows-targets 0.52.6",
1390
+]
1391
+
1392
+[[package]]
1393
+name = "windows-sys"
1394
+version = "0.60.2"
1395
+source = "registry+https://github.com/rust-lang/crates.io-index"
1396
+checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
1397
+dependencies = [
1398
+ "windows-targets 0.53.5",
1399
+]
1400
+
1401
+[[package]]
1402
+name = "windows-sys"
1403
+version = "0.61.2"
1404
+source = "registry+https://github.com/rust-lang/crates.io-index"
1405
+checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
1406
+dependencies = [
1407
+ "windows-link",
1408
+]
1409
+
1410
+[[package]]
1411
+name = "windows-targets"
1412
+version = "0.48.5"
1413
+source = "registry+https://github.com/rust-lang/crates.io-index"
1414
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
1415
+dependencies = [
1416
+ "windows_aarch64_gnullvm 0.48.5",
1417
+ "windows_aarch64_msvc 0.48.5",
1418
+ "windows_i686_gnu 0.48.5",
1419
+ "windows_i686_msvc 0.48.5",
1420
+ "windows_x86_64_gnu 0.48.5",
1421
+ "windows_x86_64_gnullvm 0.48.5",
1422
+ "windows_x86_64_msvc 0.48.5",
1423
+]
1424
+
1425
+[[package]]
1426
+name = "windows-targets"
1427
+version = "0.52.6"
1428
+source = "registry+https://github.com/rust-lang/crates.io-index"
1429
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
1430
+dependencies = [
1431
+ "windows_aarch64_gnullvm 0.52.6",
1432
+ "windows_aarch64_msvc 0.52.6",
1433
+ "windows_i686_gnu 0.52.6",
1434
+ "windows_i686_gnullvm 0.52.6",
1435
+ "windows_i686_msvc 0.52.6",
1436
+ "windows_x86_64_gnu 0.52.6",
1437
+ "windows_x86_64_gnullvm 0.52.6",
1438
+ "windows_x86_64_msvc 0.52.6",
1439
+]
1440
+
1441
+[[package]]
1442
+name = "windows-targets"
1443
+version = "0.53.5"
1444
+source = "registry+https://github.com/rust-lang/crates.io-index"
1445
+checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
1446
+dependencies = [
1447
+ "windows-link",
1448
+ "windows_aarch64_gnullvm 0.53.1",
1449
+ "windows_aarch64_msvc 0.53.1",
1450
+ "windows_i686_gnu 0.53.1",
1451
+ "windows_i686_gnullvm 0.53.1",
1452
+ "windows_i686_msvc 0.53.1",
1453
+ "windows_x86_64_gnu 0.53.1",
1454
+ "windows_x86_64_gnullvm 0.53.1",
1455
+ "windows_x86_64_msvc 0.53.1",
1456
+]
1457
+
1458
+[[package]]
1459
+name = "windows_aarch64_gnullvm"
1460
+version = "0.48.5"
1461
+source = "registry+https://github.com/rust-lang/crates.io-index"
1462
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
1463
+
1464
+[[package]]
1465
+name = "windows_aarch64_gnullvm"
1466
+version = "0.52.6"
1467
+source = "registry+https://github.com/rust-lang/crates.io-index"
1468
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
1469
+
1470
+[[package]]
1471
+name = "windows_aarch64_gnullvm"
1472
+version = "0.53.1"
1473
+source = "registry+https://github.com/rust-lang/crates.io-index"
1474
+checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
1475
+
1476
+[[package]]
1477
+name = "windows_aarch64_msvc"
1478
+version = "0.48.5"
1479
+source = "registry+https://github.com/rust-lang/crates.io-index"
1480
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
1481
+
1482
+[[package]]
1483
+name = "windows_aarch64_msvc"
1484
+version = "0.52.6"
1485
+source = "registry+https://github.com/rust-lang/crates.io-index"
1486
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
1487
+
1488
+[[package]]
1489
+name = "windows_aarch64_msvc"
1490
+version = "0.53.1"
1491
+source = "registry+https://github.com/rust-lang/crates.io-index"
1492
+checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
1493
+
1494
+[[package]]
1495
+name = "windows_i686_gnu"
1496
+version = "0.48.5"
1497
+source = "registry+https://github.com/rust-lang/crates.io-index"
1498
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
1499
+
1500
+[[package]]
1501
+name = "windows_i686_gnu"
1502
+version = "0.52.6"
1503
+source = "registry+https://github.com/rust-lang/crates.io-index"
1504
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
1505
+
1506
+[[package]]
1507
+name = "windows_i686_gnu"
1508
+version = "0.53.1"
1509
+source = "registry+https://github.com/rust-lang/crates.io-index"
1510
+checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
1511
+
1512
+[[package]]
1513
+name = "windows_i686_gnullvm"
1514
+version = "0.52.6"
1515
+source = "registry+https://github.com/rust-lang/crates.io-index"
1516
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
1517
+
1518
+[[package]]
1519
+name = "windows_i686_gnullvm"
1520
+version = "0.53.1"
1521
+source = "registry+https://github.com/rust-lang/crates.io-index"
1522
+checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
1523
+
1524
+[[package]]
1525
+name = "windows_i686_msvc"
1526
+version = "0.48.5"
1527
+source = "registry+https://github.com/rust-lang/crates.io-index"
1528
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
1529
+
1530
+[[package]]
1531
+name = "windows_i686_msvc"
1532
+version = "0.52.6"
1533
+source = "registry+https://github.com/rust-lang/crates.io-index"
1534
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
1535
+
1536
+[[package]]
1537
+name = "windows_i686_msvc"
1538
+version = "0.53.1"
1539
+source = "registry+https://github.com/rust-lang/crates.io-index"
1540
+checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
1541
+
1542
+[[package]]
1543
+name = "windows_x86_64_gnu"
1544
+version = "0.48.5"
1545
+source = "registry+https://github.com/rust-lang/crates.io-index"
1546
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
1547
+
1548
+[[package]]
1549
+name = "windows_x86_64_gnu"
1550
+version = "0.52.6"
1551
+source = "registry+https://github.com/rust-lang/crates.io-index"
1552
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
1553
+
1554
+[[package]]
1555
+name = "windows_x86_64_gnu"
1556
+version = "0.53.1"
1557
+source = "registry+https://github.com/rust-lang/crates.io-index"
1558
+checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
1559
+
1560
+[[package]]
1561
+name = "windows_x86_64_gnullvm"
1562
+version = "0.48.5"
1563
+source = "registry+https://github.com/rust-lang/crates.io-index"
1564
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
1565
+
1566
+[[package]]
1567
+name = "windows_x86_64_gnullvm"
1568
+version = "0.52.6"
1569
+source = "registry+https://github.com/rust-lang/crates.io-index"
1570
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
1571
+
1572
+[[package]]
1573
+name = "windows_x86_64_gnullvm"
1574
+version = "0.53.1"
1575
+source = "registry+https://github.com/rust-lang/crates.io-index"
1576
+checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
1577
+
1578
+[[package]]
1579
+name = "windows_x86_64_msvc"
1580
+version = "0.48.5"
1581
+source = "registry+https://github.com/rust-lang/crates.io-index"
1582
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
1583
+
1584
+[[package]]
1585
+name = "windows_x86_64_msvc"
1586
+version = "0.52.6"
1587
+source = "registry+https://github.com/rust-lang/crates.io-index"
1588
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
1589
+
1590
+[[package]]
1591
+name = "windows_x86_64_msvc"
1592
+version = "0.53.1"
1593
+source = "registry+https://github.com/rust-lang/crates.io-index"
1594
+checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
1595
+
1596
+[[package]]
1597
+name = "winnow"
1598
+version = "0.7.14"
1599
+source = "registry+https://github.com/rust-lang/crates.io-index"
1600
+checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829"
1601
+dependencies = [
1602
+ "memchr",
1603
+]
1604
+
1605
+[[package]]
1606
+name = "wit-bindgen"
1607
+version = "0.46.0"
1608
+source = "registry+https://github.com/rust-lang/crates.io-index"
1609
+checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
1610
+
1611
+[[package]]
1612
+name = "xdg-home"
1613
+version = "1.3.0"
1614
+source = "registry+https://github.com/rust-lang/crates.io-index"
1615
+checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6"
1616
+dependencies = [
1617
+ "libc",
1618
+ "windows-sys 0.59.0",
1619
+]
1620
+
1621
+[[package]]
1622
+name = "zbus"
1623
+version = "4.4.0"
1624
+source = "registry+https://github.com/rust-lang/crates.io-index"
1625
+checksum = "bb97012beadd29e654708a0fdb4c84bc046f537aecfde2c3ee0a9e4b4d48c725"
1626
+dependencies = [
1627
+ "async-broadcast",
1628
+ "async-executor",
1629
+ "async-fs",
1630
+ "async-io",
1631
+ "async-lock",
1632
+ "async-process",
1633
+ "async-recursion",
1634
+ "async-task",
1635
+ "async-trait",
1636
+ "blocking",
1637
+ "enumflags2",
1638
+ "event-listener",
1639
+ "futures-core",
1640
+ "futures-sink",
1641
+ "futures-util",
1642
+ "hex",
1643
+ "nix",
1644
+ "ordered-stream",
1645
+ "rand",
1646
+ "serde",
1647
+ "serde_repr",
1648
+ "sha1",
1649
+ "static_assertions",
1650
+ "tracing",
1651
+ "uds_windows",
1652
+ "windows-sys 0.52.0",
1653
+ "xdg-home",
1654
+ "zbus_macros",
1655
+ "zbus_names",
1656
+ "zvariant",
1657
+]
1658
+
1659
+[[package]]
1660
+name = "zbus_macros"
1661
+version = "4.4.0"
1662
+source = "registry+https://github.com/rust-lang/crates.io-index"
1663
+checksum = "267db9407081e90bbfa46d841d3cbc60f59c0351838c4bc65199ecd79ab1983e"
1664
+dependencies = [
1665
+ "proc-macro-crate",
1666
+ "proc-macro2",
1667
+ "quote",
1668
+ "syn",
1669
+ "zvariant_utils",
1670
+]
1671
+
1672
+[[package]]
1673
+name = "zbus_names"
1674
+version = "3.0.0"
1675
+source = "registry+https://github.com/rust-lang/crates.io-index"
1676
+checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c"
1677
+dependencies = [
1678
+ "serde",
1679
+ "static_assertions",
1680
+ "zvariant",
1681
+]
1682
+
1683
+[[package]]
1684
+name = "zerocopy"
1685
+version = "0.8.39"
1686
+source = "registry+https://github.com/rust-lang/crates.io-index"
1687
+checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a"
1688
+dependencies = [
1689
+ "zerocopy-derive",
1690
+]
1691
+
1692
+[[package]]
1693
+name = "zerocopy-derive"
1694
+version = "0.8.39"
1695
+source = "registry+https://github.com/rust-lang/crates.io-index"
1696
+checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517"
1697
+dependencies = [
1698
+ "proc-macro2",
1699
+ "quote",
1700
+ "syn",
1701
+]
1702
+
1703
+[[package]]
1704
+name = "zmij"
1705
+version = "1.0.21"
1706
+source = "registry+https://github.com/rust-lang/crates.io-index"
1707
+checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
1708
+
1709
+[[package]]
1710
+name = "zvariant"
1711
+version = "4.2.0"
1712
+source = "registry+https://github.com/rust-lang/crates.io-index"
1713
+checksum = "2084290ab9a1c471c38fc524945837734fbf124487e105daec2bb57fd48c81fe"
1714
+dependencies = [
1715
+ "endi",
1716
+ "enumflags2",
1717
+ "serde",
1718
+ "static_assertions",
1719
+ "zvariant_derive",
1720
+]
1721
+
1722
+[[package]]
1723
+name = "zvariant_derive"
1724
+version = "4.2.0"
1725
+source = "registry+https://github.com/rust-lang/crates.io-index"
1726
+checksum = "73e2ba546bda683a90652bac4a279bc146adad1386f25379cf73200d2002c449"
1727
+dependencies = [
1728
+ "proc-macro-crate",
1729
+ "proc-macro2",
1730
+ "quote",
1731
+ "syn",
1732
+ "zvariant_utils",
1733
+]
1734
+
1735
+[[package]]
1736
+name = "zvariant_utils"
1737
+version = "2.1.0"
1738
+source = "registry+https://github.com/rust-lang/crates.io-index"
1739
+checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340"
1740
+dependencies = [
1741
+ "proc-macro2",
1742
+ "quote",
1743
+ "syn",
1744
+]
Cargo.tomladded
@@ -0,0 +1,24 @@
1
+[workspace]
2
+resolver = "2"
3
+members = ["garcard", "garcard-ipc", "garcardctl"]
4
+
5
+[workspace.package]
6
+version = "0.1.0"
7
+edition = "2024"
8
+license = "MIT"
9
+authors = ["gardesk contributors"]
10
+
11
+[workspace.dependencies]
12
+garcard-ipc = { path = "garcard-ipc" }
13
+
14
+anyhow = "1"
15
+clap = { version = "4", features = ["derive"] }
16
+serde = { version = "1", features = ["derive"] }
17
+serde_json = "1"
18
+tokio = { version = "1", features = ["full", "signal"] }
19
+tracing = "0.1"
20
+tracing-subscriber = { version = "0.3", features = ["env-filter"] }
21
+dirs = "5"
22
+toml = "0.8"
23
+zbus = "4"
24
+nix = { version = "0.29", features = ["user"] }
README.mdadded
@@ -0,0 +1,25 @@
1
+# garcard
2
+
3
+`garcard` is the in-progress Polkit authentication agent for the gar desktop suite.
4
+
5
+## Workspace
6
+1. `garcard`: daemon runtime
7
+2. `garcard-ipc`: shared protocol types
8
+3. `garcardctl`: control/debug CLI
9
+
10
+## Quick Start
11
+1. `cargo run -p garcard -- daemon`
12
+2. `cargo run -p garcardctl -- status`
13
+
14
+## Config
15
+Default config path: `~/.config/garcard/config.toml`
16
+
17
+Environment overrides:
18
+1. `GARCARD_SOCKET`
19
+2. `GARCARD_SOCKET_MODE`
20
+3. `GARCARD_CONFIG`
21
+4. `GARCARD_AGENT_BACKEND`
22
+5. `GARCARD_POLKIT_OBJECT_PATH`
23
+6. `GARCARD_LOCALE`
24
+
25
+See `examples/config.toml` for a starter file.
examples/config.tomladded
@@ -0,0 +1,22 @@
1
+# garcard example configuration
2
+
3
+# Optional explicit socket path.
4
+# socket_path = "/tmp/garcard.sock"
5
+
6
+# Socket mode in octal string form.
7
+# Typical values:
8
+# - 600: owner read/write
9
+# - 660: owner/group read/write
10
+socket_mode = "600"
11
+
12
+# Backend mode:
13
+# - auto: try polkit, then fall back to stub if unavailable
14
+# - polkit: require polkit backend registration
15
+# - stub: run with local stub backend (dev/test only)
16
+agent_backend = "auto"
17
+
18
+# Polkit registration object path.
19
+polkit_object_path = "/org/gardesk/Garcard/AuthAgent"
20
+
21
+# Locale sent to polkit authority registration.
22
+locale = "en_US.UTF-8"
garcard-ipc/Cargo.tomladded
@@ -0,0 +1,10 @@
1
+[package]
2
+name = "garcard-ipc"
3
+version.workspace = true
4
+edition.workspace = true
5
+license.workspace = true
6
+authors.workspace = true
7
+
8
+[dependencies]
9
+serde.workspace = true
10
+serde_json.workspace = true
garcard-ipc/src/lib.rsadded
@@ -0,0 +1,164 @@
1
+//! Shared IPC types for garcard daemon and control clients.
2
+
3
+use serde::{Deserialize, Serialize};
4
+use serde_json::Value;
5
+use std::ffi::OsString;
6
+use std::os::unix::net::UnixListener;
7
+use std::path::PathBuf;
8
+use std::time::{SystemTime, UNIX_EPOCH};
9
+
10
+/// Protocol version for compatibility checks.
11
+pub const PROTOCOL_VERSION: u32 = 1;
12
+
13
+/// Runtime socket filename.
14
+pub const SOCKET_BASENAME: &str = "garcard.sock";
15
+
16
+/// Commands accepted by garcard daemon.
17
+#[derive(Debug, Clone, Serialize, Deserialize)]
18
+#[serde(tag = "command", rename_all = "snake_case")]
19
+pub enum Command {
20
+    Ping,
21
+    Status,
22
+    Version,
23
+    AuthSummary,
24
+    Quit,
25
+}
26
+
27
+/// Standard daemon response envelope.
28
+#[derive(Debug, Clone, Serialize, Deserialize)]
29
+pub struct Response {
30
+    pub success: bool,
31
+    #[serde(skip_serializing_if = "Option::is_none")]
32
+    pub data: Option<Value>,
33
+    #[serde(skip_serializing_if = "Option::is_none")]
34
+    pub error: Option<String>,
35
+}
36
+
37
+impl Response {
38
+    pub fn ok() -> Self {
39
+        Self {
40
+            success: true,
41
+            data: None,
42
+            error: None,
43
+        }
44
+    }
45
+
46
+    pub fn ok_with_data(data: impl Serialize) -> Self {
47
+        Self {
48
+            success: true,
49
+            data: serde_json::to_value(data).ok(),
50
+            error: None,
51
+        }
52
+    }
53
+
54
+    pub fn err(message: impl Into<String>) -> Self {
55
+        Self {
56
+            success: false,
57
+            data: None,
58
+            error: Some(message.into()),
59
+        }
60
+    }
61
+}
62
+
63
+/// Minimal health and runtime status summary.
64
+#[derive(Debug, Clone, Serialize, Deserialize)]
65
+pub struct StatusData {
66
+    pub running: bool,
67
+    pub pid: u32,
68
+    pub uptime_secs: u64,
69
+    pub version: String,
70
+    pub protocol_version: u32,
71
+    pub socket_path: String,
72
+    pub agent_backend: String,
73
+}
74
+
75
+/// Version handshake payload.
76
+#[derive(Debug, Clone, Serialize, Deserialize)]
77
+pub struct VersionData {
78
+    pub component: String,
79
+    pub version: String,
80
+    pub protocol_version: u32,
81
+}
82
+
83
+/// Auth workflow summary with no sensitive data.
84
+#[derive(Debug, Clone, Serialize, Deserialize)]
85
+pub struct AuthSummary {
86
+    pub state: String,
87
+    pub active_requests: usize,
88
+    pub queued_requests: usize,
89
+}
90
+
91
+/// Resolve the daemon socket path from `XDG_RUNTIME_DIR` with `/tmp` fallback.
92
+pub fn socket_path() -> PathBuf {
93
+    if let Some(explicit) = std::env::var_os("GARCARD_SOCKET") {
94
+        return PathBuf::from(explicit);
95
+    }
96
+
97
+    let runtime_dir = std::env::var_os("XDG_RUNTIME_DIR");
98
+    if runtime_dir.as_ref().is_some_and(runtime_dir_is_usable) {
99
+        return socket_path_from_runtime_dir(runtime_dir);
100
+    }
101
+
102
+    socket_path_from_runtime_dir(None)
103
+}
104
+
105
+/// Build socket path from an explicit runtime dir value.
106
+pub fn socket_path_from_runtime_dir(runtime_dir: Option<OsString>) -> PathBuf {
107
+    match runtime_dir {
108
+        Some(dir) => PathBuf::from(dir).join(SOCKET_BASENAME),
109
+        None => PathBuf::from("/tmp").join(SOCKET_BASENAME),
110
+    }
111
+}
112
+
113
+/// Check if we can create a socket in runtime dir.
114
+///
115
+/// In sandboxed environments, `XDG_RUNTIME_DIR` can exist but still reject
116
+/// socket creation. We probe with a unique temporary socket path so caller
117
+/// code can safely fall back to `/tmp`.
118
+fn runtime_dir_is_usable(runtime_dir: &OsString) -> bool {
119
+    let base = PathBuf::from(runtime_dir);
120
+    let timestamp = SystemTime::now()
121
+        .duration_since(UNIX_EPOCH)
122
+        .ok()
123
+        .map(|d| d.as_nanos())
124
+        .unwrap_or(0);
125
+    let probe = base.join(format!(
126
+        ".garcard-probe-{}-{}.sock",
127
+        std::process::id(),
128
+        timestamp
129
+    ));
130
+
131
+    match UnixListener::bind(&probe) {
132
+        Ok(listener) => {
133
+            drop(listener);
134
+            let _ = std::fs::remove_file(&probe);
135
+            true
136
+        }
137
+        Err(_) => false,
138
+    }
139
+}
140
+
141
+#[cfg(test)]
142
+mod tests {
143
+    use super::*;
144
+
145
+    #[test]
146
+    fn command_round_trip() {
147
+        let cmd = Command::AuthSummary;
148
+        let encoded = serde_json::to_string(&cmd).expect("encode command");
149
+        let decoded: Command = serde_json::from_str(&encoded).expect("decode command");
150
+        assert!(matches!(decoded, Command::AuthSummary));
151
+    }
152
+
153
+    #[test]
154
+    fn socket_path_falls_back_to_tmp() {
155
+        let path = socket_path_from_runtime_dir(None);
156
+        assert_eq!(path, PathBuf::from("/tmp/garcard.sock"));
157
+    }
158
+
159
+    #[test]
160
+    fn socket_path_uses_runtime_dir() {
161
+        let path = socket_path_from_runtime_dir(Some(OsString::from("/run/user/1000")));
162
+        assert_eq!(path, PathBuf::from("/run/user/1000/garcard.sock"));
163
+    }
164
+}
garcard.serviceadded
@@ -0,0 +1,14 @@
1
+[Unit]
2
+Description=garcard polkit auth agent
3
+Documentation=https://github.com/gardesk/gardesk
4
+After=graphical-session.target
5
+PartOf=graphical-session.target
6
+
7
+[Service]
8
+Type=simple
9
+ExecStart=garcard daemon
10
+Restart=on-failure
11
+RestartSec=2
12
+
13
+[Install]
14
+WantedBy=graphical-session.target
garcard/Cargo.tomladded
@@ -0,0 +1,21 @@
1
+[package]
2
+name = "garcard"
3
+version.workspace = true
4
+edition.workspace = true
5
+license.workspace = true
6
+authors.workspace = true
7
+
8
+[dependencies]
9
+garcard-ipc.workspace = true
10
+
11
+anyhow.workspace = true
12
+clap.workspace = true
13
+serde_json.workspace = true
14
+tokio.workspace = true
15
+tracing.workspace = true
16
+tracing-subscriber.workspace = true
17
+dirs.workspace = true
18
+toml.workspace = true
19
+serde.workspace = true
20
+zbus.workspace = true
21
+nix.workspace = true
garcard/src/agent.rsadded
@@ -0,0 +1,185 @@
1
+use anyhow::Result;
2
+use std::collections::HashMap;
3
+use zbus::blocking::{Connection, Proxy};
4
+use zbus::zvariant::{OwnedObjectPath, OwnedValue};
5
+
6
+/// Backend interface for Polkit agent integration.
7
+pub trait AuthAgentBackend {
8
+    fn name(&self) -> &'static str;
9
+    fn register(&mut self) -> Result<()>;
10
+    fn unregister(&mut self) -> Result<()>;
11
+}
12
+
13
+/// Placeholder backend used during Sprint 01 scaffolding.
14
+#[derive(Default)]
15
+pub struct StubPolkitAgent;
16
+
17
+impl AuthAgentBackend for StubPolkitAgent {
18
+    fn name(&self) -> &'static str {
19
+        "stub-polkit-agent"
20
+    }
21
+
22
+    fn register(&mut self) -> Result<()> {
23
+        tracing::info!("Registered stub auth-agent backend");
24
+        Ok(())
25
+    }
26
+
27
+    fn unregister(&mut self) -> Result<()> {
28
+        tracing::info!("Unregistered stub auth-agent backend");
29
+        Ok(())
30
+    }
31
+}
32
+
33
+/// Runtime configuration for the real Polkit backend.
34
+#[derive(Debug, Clone)]
35
+pub struct PolkitBackendConfig {
36
+    pub object_path: String,
37
+    pub locale: String,
38
+}
39
+
40
+type Subject = (String, HashMap<String, OwnedValue>);
41
+
42
+/// Real Polkit backend scaffold based on org.freedesktop.PolicyKit1 APIs.
43
+///
44
+/// This registers/unregisters an authentication agent with the authority.
45
+/// The request handling object is the next implementation phase.
46
+pub struct PolkitAgent {
47
+    object_path: OwnedObjectPath,
48
+    locale: String,
49
+    subject: Subject,
50
+    connection: Option<Connection>,
51
+    registered: bool,
52
+}
53
+
54
+impl PolkitAgent {
55
+    pub fn new(config: PolkitBackendConfig) -> Result<Self> {
56
+        let object_path = OwnedObjectPath::try_from(config.object_path)
57
+            .map_err(|err| anyhow::anyhow!("invalid polkit object path: {}", err))?;
58
+
59
+        Ok(Self {
60
+            object_path,
61
+            locale: config.locale,
62
+            subject: build_subject(),
63
+            connection: None,
64
+            registered: false,
65
+        })
66
+    }
67
+
68
+    fn proxy(connection: &Connection) -> Result<Proxy<'_>> {
69
+        let proxy = Proxy::new(
70
+            connection,
71
+            "org.freedesktop.PolicyKit1",
72
+            "/org/freedesktop/PolicyKit1/Authority",
73
+            "org.freedesktop.PolicyKit1.Authority",
74
+        )?;
75
+        Ok(proxy)
76
+    }
77
+}
78
+
79
+impl AuthAgentBackend for PolkitAgent {
80
+    fn name(&self) -> &'static str {
81
+        "polkit"
82
+    }
83
+
84
+    fn register(&mut self) -> Result<()> {
85
+        if self.registered {
86
+            return Ok(());
87
+        }
88
+
89
+        let connection = Connection::system()?;
90
+        {
91
+            let proxy = Self::proxy(&connection)?;
92
+            let _: () = proxy.call(
93
+                "RegisterAuthenticationAgent",
94
+                &(
95
+                    &self.subject,
96
+                    self.locale.as_str(),
97
+                    self.object_path.clone(),
98
+                ),
99
+            )?;
100
+        }
101
+
102
+        self.connection = Some(connection);
103
+        self.registered = true;
104
+        tracing::info!(
105
+            backend = self.name(),
106
+            "Registered polkit authentication agent"
107
+        );
108
+        Ok(())
109
+    }
110
+
111
+    fn unregister(&mut self) -> Result<()> {
112
+        if !self.registered {
113
+            return Ok(());
114
+        }
115
+
116
+        if let Some(connection) = &self.connection {
117
+            match Self::proxy(connection)?.call::<_, _, ()>(
118
+                "UnregisterAuthenticationAgent",
119
+                &(
120
+                    &self.subject,
121
+                    self.locale.as_str(),
122
+                    self.object_path.clone(),
123
+                ),
124
+            ) {
125
+                Ok(()) => {
126
+                    tracing::info!(
127
+                        backend = self.name(),
128
+                        "Unregistered polkit authentication agent"
129
+                    );
130
+                }
131
+                Err(err) => {
132
+                    tracing::warn!(error = %err, "Failed to unregister polkit authentication agent");
133
+                }
134
+            }
135
+        }
136
+
137
+        self.registered = false;
138
+        self.connection = None;
139
+        Ok(())
140
+    }
141
+}
142
+
143
+fn build_subject() -> Subject {
144
+    let mut details = HashMap::new();
145
+    details.insert("pid".to_string(), OwnedValue::from(std::process::id()));
146
+    details.insert(
147
+        "uid".to_string(),
148
+        OwnedValue::from(nix::unistd::geteuid().as_raw()),
149
+    );
150
+    ("unix-process".to_string(), details)
151
+}
152
+
153
+#[cfg(test)]
154
+mod tests {
155
+    use super::*;
156
+
157
+    #[test]
158
+    fn polkit_agent_rejects_invalid_object_path() {
159
+        let result = PolkitAgent::new(PolkitBackendConfig {
160
+            object_path: "invalid path".to_string(),
161
+            locale: "C".to_string(),
162
+        });
163
+        assert!(result.is_err());
164
+    }
165
+
166
+    #[test]
167
+    fn subject_uses_unix_process_kind() {
168
+        let subject = build_subject();
169
+        assert_eq!(subject.0, "unix-process");
170
+        assert!(subject.1.contains_key("pid"));
171
+        assert!(subject.1.contains_key("uid"));
172
+    }
173
+
174
+    #[test]
175
+    fn invalid_object_path_error_message_mentions_path() {
176
+        let err = match PolkitAgent::new(PolkitBackendConfig {
177
+            object_path: "invalid path".to_string(),
178
+            locale: "C".to_string(),
179
+        }) {
180
+            Ok(_) => panic!("invalid object path should fail"),
181
+            Err(err) => err,
182
+        };
183
+        assert!(err.to_string().contains("invalid polkit object path"));
184
+    }
185
+}
garcard/src/config.rsadded
@@ -0,0 +1,209 @@
1
+use anyhow::{Context, Result};
2
+use garcard_ipc::socket_path;
3
+use serde::Deserialize;
4
+use std::fmt;
5
+use std::path::PathBuf;
6
+
7
+/// Runtime configuration for the garcard daemon.
8
+#[derive(Debug, Clone)]
9
+pub struct Config {
10
+    pub socket_path: PathBuf,
11
+    pub socket_mode: u32,
12
+    pub agent_backend: AgentBackendMode,
13
+    pub polkit_object_path: String,
14
+    pub locale: String,
15
+}
16
+
17
+impl Default for Config {
18
+    fn default() -> Self {
19
+        Self {
20
+            socket_path: socket_path(),
21
+            socket_mode: 0o600,
22
+            agent_backend: AgentBackendMode::Auto,
23
+            polkit_object_path: "/org/gardesk/Garcard/AuthAgent".to_string(),
24
+            locale: std::env::var("LANG").unwrap_or_else(|_| "en_US.UTF-8".to_string()),
25
+        }
26
+    }
27
+}
28
+
29
+impl Config {
30
+    pub fn load() -> Result<Self> {
31
+        let mut cfg = Self::default();
32
+        cfg.apply_file_config()?;
33
+
34
+        if let Some(raw_mode) = std::env::var_os("GARCARD_SOCKET_MODE") {
35
+            let parsed = parse_octal_mode(&raw_mode.to_string_lossy())
36
+                .with_context(|| "Invalid GARCARD_SOCKET_MODE (expected octal, e.g. 600)")?;
37
+            cfg.socket_mode = parsed;
38
+        }
39
+
40
+        if let Some(raw_socket) = std::env::var_os("GARCARD_SOCKET") {
41
+            cfg.socket_path = PathBuf::from(raw_socket);
42
+        }
43
+        if let Some(raw_backend) = std::env::var_os("GARCARD_AGENT_BACKEND") {
44
+            cfg.agent_backend = AgentBackendMode::from_str(&raw_backend.to_string_lossy())
45
+                .with_context(|| "Invalid GARCARD_AGENT_BACKEND (expected auto|polkit|stub)")?;
46
+        }
47
+        if let Some(raw_object_path) = std::env::var_os("GARCARD_POLKIT_OBJECT_PATH") {
48
+            cfg.polkit_object_path = raw_object_path.to_string_lossy().to_string();
49
+        }
50
+        if let Some(raw_locale) = std::env::var_os("GARCARD_LOCALE") {
51
+            cfg.locale = raw_locale.to_string_lossy().to_string();
52
+        }
53
+
54
+        Ok(cfg)
55
+    }
56
+
57
+    fn apply_file_config(&mut self) -> Result<()> {
58
+        let Some(path) = config_path() else {
59
+            return Ok(());
60
+        };
61
+        if !path.exists() {
62
+            return Ok(());
63
+        }
64
+
65
+        let content = std::fs::read_to_string(&path)
66
+            .with_context(|| format!("Failed to read config file {}", path.display()))?;
67
+        let file_cfg: FileConfig = toml::from_str(&content)
68
+            .with_context(|| format!("Failed to parse config file {}", path.display()))?;
69
+
70
+        if let Some(socket_path) = file_cfg.socket_path {
71
+            self.socket_path = PathBuf::from(socket_path);
72
+        }
73
+        if let Some(socket_mode) = file_cfg.socket_mode {
74
+            self.socket_mode = parse_octal_mode(&socket_mode)
75
+                .with_context(|| "Invalid socket_mode in config file (expected octal)")?;
76
+        }
77
+        if let Some(agent_backend) = file_cfg.agent_backend {
78
+            self.agent_backend = AgentBackendMode::from_str(&agent_backend)
79
+                .with_context(|| "Invalid agent_backend in config file")?;
80
+        }
81
+        if let Some(polkit_object_path) = file_cfg.polkit_object_path {
82
+            self.polkit_object_path = polkit_object_path;
83
+        }
84
+        if let Some(locale) = file_cfg.locale {
85
+            self.locale = locale;
86
+        }
87
+
88
+        Ok(())
89
+    }
90
+}
91
+
92
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
93
+pub enum AgentBackendMode {
94
+    Auto,
95
+    Polkit,
96
+    Stub,
97
+}
98
+
99
+impl AgentBackendMode {
100
+    fn from_str(raw: &str) -> Result<Self> {
101
+        match raw.trim().to_lowercase().as_str() {
102
+            "auto" => Ok(Self::Auto),
103
+            "polkit" => Ok(Self::Polkit),
104
+            "stub" => Ok(Self::Stub),
105
+            other => anyhow::bail!("unsupported backend mode: {}", other),
106
+        }
107
+    }
108
+}
109
+
110
+impl fmt::Display for AgentBackendMode {
111
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112
+        let value = match self {
113
+            Self::Auto => "auto",
114
+            Self::Polkit => "polkit",
115
+            Self::Stub => "stub",
116
+        };
117
+        write!(f, "{}", value)
118
+    }
119
+}
120
+
121
+#[derive(Debug, Clone, Deserialize, Default)]
122
+#[serde(default)]
123
+struct FileConfig {
124
+    socket_path: Option<String>,
125
+    socket_mode: Option<String>,
126
+    agent_backend: Option<String>,
127
+    polkit_object_path: Option<String>,
128
+    locale: Option<String>,
129
+}
130
+
131
+fn config_path() -> Option<PathBuf> {
132
+    if let Some(explicit) = std::env::var_os("GARCARD_CONFIG") {
133
+        return Some(PathBuf::from(explicit));
134
+    }
135
+
136
+    dirs::config_dir().map(|base| base.join("garcard/config.toml"))
137
+}
138
+
139
+fn parse_octal_mode(raw: &str) -> Result<u32> {
140
+    let trimmed = raw.trim();
141
+    let mode = u32::from_str_radix(trimmed, 8)
142
+        .with_context(|| format!("failed to parse mode value: {trimmed}"))?;
143
+    Ok(mode)
144
+}
145
+
146
+#[cfg(test)]
147
+mod tests {
148
+    use super::*;
149
+
150
+    #[test]
151
+    fn parse_mode_octal() {
152
+        let mode = parse_octal_mode("640").expect("valid mode");
153
+        assert_eq!(mode, 0o640);
154
+    }
155
+
156
+    #[test]
157
+    fn parse_mode_rejects_invalid() {
158
+        let err = parse_octal_mode("x00").expect_err("should fail");
159
+        assert!(err.to_string().contains("failed to parse mode value"));
160
+    }
161
+
162
+    #[test]
163
+    fn parse_file_config_values() {
164
+        let parsed: FileConfig = toml::from_str(
165
+            r#"
166
+socket_path = "/tmp/custom-garcard.sock"
167
+socket_mode = "640"
168
+agent_backend = "stub"
169
+polkit_object_path = "/org/gardesk/Garcard/TestAgent"
170
+locale = "C"
171
+"#,
172
+        )
173
+        .expect("parse file config");
174
+
175
+        assert_eq!(
176
+            parsed.socket_path.as_deref(),
177
+            Some("/tmp/custom-garcard.sock")
178
+        );
179
+        assert_eq!(parsed.socket_mode.as_deref(), Some("640"));
180
+        assert_eq!(parsed.agent_backend.as_deref(), Some("stub"));
181
+        assert_eq!(
182
+            parsed.polkit_object_path.as_deref(),
183
+            Some("/org/gardesk/Garcard/TestAgent")
184
+        );
185
+        assert_eq!(parsed.locale.as_deref(), Some("C"));
186
+    }
187
+
188
+    #[test]
189
+    fn backend_mode_parsing() {
190
+        assert_eq!(
191
+            AgentBackendMode::from_str("auto").expect("auto"),
192
+            AgentBackendMode::Auto
193
+        );
194
+        assert_eq!(
195
+            AgentBackendMode::from_str("polkit").expect("polkit"),
196
+            AgentBackendMode::Polkit
197
+        );
198
+        assert_eq!(
199
+            AgentBackendMode::from_str("stub").expect("stub"),
200
+            AgentBackendMode::Stub
201
+        );
202
+    }
203
+
204
+    #[test]
205
+    fn backend_mode_rejects_unknown() {
206
+        let err = AgentBackendMode::from_str("bad").expect_err("should fail");
207
+        assert!(err.to_string().contains("unsupported backend mode"));
208
+    }
209
+}
garcard/src/daemon.rsadded
@@ -0,0 +1,278 @@
1
+use crate::agent::{AuthAgentBackend, PolkitAgent, PolkitBackendConfig, StubPolkitAgent};
2
+use crate::config::{AgentBackendMode, Config};
3
+use crate::state::RuntimeState;
4
+use anyhow::{Context, Result};
5
+use garcard_ipc::{Command, Response};
6
+use serde_json::json;
7
+use std::os::unix::fs::PermissionsExt;
8
+use std::path::{Path, PathBuf};
9
+use std::sync::Arc;
10
+use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
11
+use tokio::net::{UnixListener, UnixStream};
12
+use tokio::sync::mpsc;
13
+
14
+/// Removes daemon socket on process exit.
15
+struct SocketGuard {
16
+    path: PathBuf,
17
+}
18
+
19
+impl SocketGuard {
20
+    fn new(path: PathBuf) -> Self {
21
+        Self { path }
22
+    }
23
+}
24
+
25
+impl Drop for SocketGuard {
26
+    fn drop(&mut self) {
27
+        let _ = std::fs::remove_file(&self.path);
28
+    }
29
+}
30
+
31
+pub async fn run(config: Config) -> Result<()> {
32
+    let mut backend = init_backend(&config)?;
33
+
34
+    prepare_socket(&config.socket_path).await?;
35
+    if let Some(parent) = config.socket_path.parent() {
36
+        tokio::fs::create_dir_all(parent)
37
+            .await
38
+            .with_context(|| format!("Failed to create socket directory {}", parent.display()))?;
39
+    }
40
+
41
+    let listener = UnixListener::bind(&config.socket_path)
42
+        .with_context(|| format!("Failed to bind socket {}", config.socket_path.display()))?;
43
+    std::fs::set_permissions(
44
+        &config.socket_path,
45
+        std::fs::Permissions::from_mode(config.socket_mode),
46
+    )
47
+    .with_context(|| {
48
+        format!(
49
+            "Failed to set socket permissions for {}",
50
+            config.socket_path.display()
51
+        )
52
+    })?;
53
+
54
+    let _socket_guard = SocketGuard::new(config.socket_path.clone());
55
+    let state = Arc::new(RuntimeState::new(
56
+        config.socket_path.display().to_string(),
57
+        backend.name(),
58
+    ));
59
+    let (shutdown_tx, mut shutdown_rx) = mpsc::unbounded_channel::<()>();
60
+
61
+    let mut sigterm = tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate())
62
+        .context("Failed to register SIGTERM handler")?;
63
+    let mut sigint = tokio::signal::unix::signal(tokio::signal::unix::SignalKind::interrupt())
64
+        .context("Failed to register SIGINT handler")?;
65
+
66
+    tracing::info!(
67
+        socket = %config.socket_path.display(),
68
+        pid = state.status().pid,
69
+        backend = backend.name(),
70
+        "garcard daemon started"
71
+    );
72
+
73
+    loop {
74
+        tokio::select! {
75
+            _ = sigterm.recv() => {
76
+                tracing::info!("Received SIGTERM");
77
+                break;
78
+            }
79
+            _ = sigint.recv() => {
80
+                tracing::info!("Received SIGINT");
81
+                break;
82
+            }
83
+            maybe_shutdown = shutdown_rx.recv() => {
84
+                if maybe_shutdown.is_some() {
85
+                    tracing::info!("Shutdown requested via IPC");
86
+                } else {
87
+                    tracing::info!("Shutdown channel closed");
88
+                }
89
+                break;
90
+            }
91
+            accept = listener.accept() => {
92
+                match accept {
93
+                    Ok((stream, _)) => {
94
+                        let runtime = Arc::clone(&state);
95
+                        let shutdown = shutdown_tx.clone();
96
+                        tokio::spawn(async move {
97
+                            if let Err(err) = handle_client(stream, runtime, shutdown).await {
98
+                                tracing::warn!(error = %err, "Client request failed");
99
+                            }
100
+                        });
101
+                    }
102
+                    Err(err) => {
103
+                        tracing::error!(error = %err, "Failed to accept IPC client");
104
+                    }
105
+                }
106
+            }
107
+        }
108
+    }
109
+
110
+    backend.unregister()?;
111
+    tracing::info!("garcard daemon stopped");
112
+    Ok(())
113
+}
114
+
115
+fn init_backend(config: &Config) -> Result<Box<dyn AuthAgentBackend>> {
116
+    match config.agent_backend {
117
+        AgentBackendMode::Stub => {
118
+            let mut backend: Box<dyn AuthAgentBackend> = Box::new(StubPolkitAgent);
119
+            backend.register()?;
120
+            Ok(backend)
121
+        }
122
+        AgentBackendMode::Polkit => {
123
+            let mut backend: Box<dyn AuthAgentBackend> =
124
+                Box::new(PolkitAgent::new(PolkitBackendConfig {
125
+                    object_path: config.polkit_object_path.clone(),
126
+                    locale: config.locale.clone(),
127
+                })?);
128
+            backend.register()?;
129
+            Ok(backend)
130
+        }
131
+        AgentBackendMode::Auto => {
132
+            let attempt = (|| -> Result<Box<dyn AuthAgentBackend>> {
133
+                let mut backend: Box<dyn AuthAgentBackend> =
134
+                    Box::new(PolkitAgent::new(PolkitBackendConfig {
135
+                        object_path: config.polkit_object_path.clone(),
136
+                        locale: config.locale.clone(),
137
+                    })?);
138
+                backend.register()?;
139
+                Ok(backend)
140
+            })();
141
+
142
+            match attempt {
143
+                Ok(backend) => Ok(backend),
144
+                Err(err) => {
145
+                    tracing::warn!(
146
+                        error = %err,
147
+                        "Failed to initialize polkit backend, falling back to stub"
148
+                    );
149
+                    let mut fallback: Box<dyn AuthAgentBackend> = Box::new(StubPolkitAgent);
150
+                    fallback.register()?;
151
+                    Ok(fallback)
152
+                }
153
+            }
154
+        }
155
+    }
156
+}
157
+
158
+async fn prepare_socket(path: &Path) -> Result<()> {
159
+    if !path.exists() {
160
+        return Ok(());
161
+    }
162
+
163
+    match UnixStream::connect(path).await {
164
+        Ok(_) => {
165
+            anyhow::bail!(
166
+                "garcard daemon is already running (socket: {})",
167
+                path.display()
168
+            );
169
+        }
170
+        Err(_) => {
171
+            tokio::fs::remove_file(path)
172
+                .await
173
+                .with_context(|| format!("Failed to remove stale socket {}", path.display()))?;
174
+            tracing::warn!(socket = %path.display(), "Removed stale socket");
175
+        }
176
+    }
177
+
178
+    Ok(())
179
+}
180
+
181
+async fn handle_client(
182
+    stream: UnixStream,
183
+    state: Arc<RuntimeState>,
184
+    shutdown_tx: mpsc::UnboundedSender<()>,
185
+) -> Result<()> {
186
+    let (reader, mut writer) = stream.into_split();
187
+    let mut reader = BufReader::new(reader);
188
+    let mut line = String::new();
189
+
190
+    let bytes = reader
191
+        .read_line(&mut line)
192
+        .await
193
+        .context("Failed to read command")?;
194
+    if bytes == 0 {
195
+        return Ok(());
196
+    }
197
+
198
+    let command: Command = serde_json::from_str(line.trim()).context("Invalid command payload")?;
199
+    let response = dispatch(command, &state, &shutdown_tx);
200
+    let payload = serde_json::to_string(&response).context("Failed to encode response")?;
201
+
202
+    writer
203
+        .write_all(payload.as_bytes())
204
+        .await
205
+        .context("Failed to write response")?;
206
+    writer
207
+        .write_all(b"\n")
208
+        .await
209
+        .context("Failed to terminate response line")?;
210
+    writer.flush().await.context("Failed to flush response")?;
211
+
212
+    Ok(())
213
+}
214
+
215
+fn dispatch(
216
+    command: Command,
217
+    state: &RuntimeState,
218
+    shutdown_tx: &mpsc::UnboundedSender<()>,
219
+) -> Response {
220
+    match command {
221
+        Command::Ping => Response::ok_with_data(json!({ "pong": true })),
222
+        Command::Status => Response::ok_with_data(state.status()),
223
+        Command::Version => Response::ok_with_data(state.version()),
224
+        Command::AuthSummary => Response::ok_with_data(state.auth_summary()),
225
+        Command::Quit => {
226
+            if shutdown_tx.send(()).is_err() {
227
+                Response::err("daemon shutdown channel unavailable")
228
+            } else {
229
+                Response::ok_with_data(json!({ "message": "shutdown_requested" }))
230
+            }
231
+        }
232
+    }
233
+}
234
+
235
+#[cfg(test)]
236
+mod tests {
237
+    use super::*;
238
+
239
+    fn fake_state() -> RuntimeState {
240
+        RuntimeState::new("/tmp/garcard-test.sock".to_string(), "test-backend")
241
+    }
242
+
243
+    #[test]
244
+    fn dispatch_ping_returns_pong() {
245
+        let state = fake_state();
246
+        let (shutdown_tx, _shutdown_rx) = mpsc::unbounded_channel();
247
+
248
+        let response = dispatch(Command::Ping, &state, &shutdown_tx);
249
+        assert!(response.success);
250
+
251
+        let data = response.data.expect("data");
252
+        assert_eq!(data.get("pong").and_then(|v| v.as_bool()), Some(true));
253
+    }
254
+
255
+    #[test]
256
+    fn dispatch_quit_signals_shutdown() {
257
+        let state = fake_state();
258
+        let (shutdown_tx, mut shutdown_rx) = mpsc::unbounded_channel();
259
+
260
+        let response = dispatch(Command::Quit, &state, &shutdown_tx);
261
+        assert!(response.success);
262
+        assert!(shutdown_rx.try_recv().is_ok());
263
+    }
264
+
265
+    #[test]
266
+    fn auto_backend_falls_back_to_stub_for_bad_object_path() {
267
+        let config = Config {
268
+            socket_path: PathBuf::from("/tmp/garcard-test.sock"),
269
+            socket_mode: 0o600,
270
+            agent_backend: AgentBackendMode::Auto,
271
+            polkit_object_path: "invalid path".to_string(),
272
+            locale: "C".to_string(),
273
+        };
274
+
275
+        let backend = init_backend(&config).expect("auto mode should fall back");
276
+        assert_eq!(backend.name(), "stub-polkit-agent");
277
+    }
278
+}
garcard/src/main.rsadded
@@ -0,0 +1,56 @@
1
+mod agent;
2
+mod config;
3
+mod daemon;
4
+mod state;
5
+
6
+use anyhow::Result;
7
+use clap::{Parser, Subcommand};
8
+use tracing_subscriber::EnvFilter;
9
+
10
+#[derive(Parser, Debug)]
11
+#[command(
12
+    name = "garcard",
13
+    about = "Polkit auth agent daemon for the gar desktop suite"
14
+)]
15
+struct Cli {
16
+    #[command(subcommand)]
17
+    command: Option<Commands>,
18
+
19
+    /// Increase logging verbosity
20
+    #[arg(short, long, action = clap::ArgAction::Count)]
21
+    verbose: u8,
22
+}
23
+
24
+#[derive(Subcommand, Debug)]
25
+enum Commands {
26
+    /// Start daemon mode
27
+    Daemon,
28
+}
29
+
30
+#[tokio::main]
31
+async fn main() -> Result<()> {
32
+    let cli = Cli::parse();
33
+    init_logging(cli.verbose);
34
+
35
+    let command = cli.command.unwrap_or(Commands::Daemon);
36
+    match command {
37
+        Commands::Daemon => {
38
+            let config = config::Config::load()?;
39
+            daemon::run(config).await
40
+        }
41
+    }
42
+}
43
+
44
+fn init_logging(verbosity: u8) {
45
+    let filter = match verbosity {
46
+        0 => "garcard=info",
47
+        1 => "garcard=debug",
48
+        _ => "garcard=trace",
49
+    };
50
+
51
+    let env_filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new(filter));
52
+    tracing_subscriber::fmt()
53
+        .with_env_filter(env_filter)
54
+        .with_target(false)
55
+        .init();
56
+}
garcard/src/state.rsadded
@@ -0,0 +1,135 @@
1
+use garcard_ipc::{AuthSummary, PROTOCOL_VERSION, StatusData, VersionData};
2
+use std::sync::RwLock;
3
+use std::sync::atomic::{AtomicUsize, Ordering};
4
+use std::time::Instant;
5
+
6
+/// Tracks auth workflow state with no sensitive data.
7
+#[derive(Debug)]
8
+pub struct AuthState {
9
+    current_state: RwLock<String>,
10
+    active_requests: AtomicUsize,
11
+    queued_requests: AtomicUsize,
12
+}
13
+
14
+impl Default for AuthState {
15
+    fn default() -> Self {
16
+        Self {
17
+            current_state: RwLock::new("idle".to_string()),
18
+            active_requests: AtomicUsize::new(0),
19
+            queued_requests: AtomicUsize::new(0),
20
+        }
21
+    }
22
+}
23
+
24
+impl AuthState {
25
+    pub fn set_state(&self, next: impl Into<String>) {
26
+        if let Ok(mut state) = self.current_state.write() {
27
+            *state = next.into();
28
+        }
29
+    }
30
+
31
+    pub fn set_active_requests(&self, count: usize) {
32
+        self.active_requests.store(count, Ordering::Relaxed);
33
+    }
34
+
35
+    pub fn set_queued_requests(&self, count: usize) {
36
+        self.queued_requests.store(count, Ordering::Relaxed);
37
+    }
38
+
39
+    pub fn summary(&self) -> AuthSummary {
40
+        let state = self
41
+            .current_state
42
+            .read()
43
+            .map(|value| value.clone())
44
+            .unwrap_or_else(|_| "unknown".to_string());
45
+
46
+        AuthSummary {
47
+            state,
48
+            active_requests: self.active_requests.load(Ordering::Relaxed),
49
+            queued_requests: self.queued_requests.load(Ordering::Relaxed),
50
+        }
51
+    }
52
+}
53
+
54
+/// Immutable process/runtime metadata for IPC status.
55
+#[derive(Debug)]
56
+pub struct RuntimeState {
57
+    started_at: Instant,
58
+    pid: u32,
59
+    socket_path: String,
60
+    backend_name: &'static str,
61
+    auth: AuthState,
62
+}
63
+
64
+impl RuntimeState {
65
+    pub fn new(socket_path: String, backend_name: &'static str) -> Self {
66
+        let auth = AuthState::default();
67
+        auth.set_state("idle");
68
+        auth.set_active_requests(0);
69
+        auth.set_queued_requests(0);
70
+
71
+        Self {
72
+            started_at: Instant::now(),
73
+            pid: std::process::id(),
74
+            socket_path,
75
+            backend_name,
76
+            auth,
77
+        }
78
+    }
79
+
80
+    pub fn status(&self) -> StatusData {
81
+        StatusData {
82
+            running: true,
83
+            pid: self.pid,
84
+            uptime_secs: self.started_at.elapsed().as_secs(),
85
+            version: env!("CARGO_PKG_VERSION").to_string(),
86
+            protocol_version: PROTOCOL_VERSION,
87
+            socket_path: self.socket_path.clone(),
88
+            agent_backend: self.backend_name.to_string(),
89
+        }
90
+    }
91
+
92
+    pub fn version(&self) -> VersionData {
93
+        VersionData {
94
+            component: "garcard".to_string(),
95
+            version: env!("CARGO_PKG_VERSION").to_string(),
96
+            protocol_version: PROTOCOL_VERSION,
97
+        }
98
+    }
99
+
100
+    pub fn auth_summary(&self) -> AuthSummary {
101
+        self.auth.summary()
102
+    }
103
+
104
+    #[allow(dead_code)]
105
+    pub fn auth_mutation(&self) -> &AuthState {
106
+        &self.auth
107
+    }
108
+}
109
+
110
+#[cfg(test)]
111
+mod tests {
112
+    use super::*;
113
+
114
+    #[test]
115
+    fn auth_state_defaults_to_idle() {
116
+        let state = AuthState::default();
117
+        let summary = state.summary();
118
+        assert_eq!(summary.state, "idle");
119
+        assert_eq!(summary.active_requests, 0);
120
+        assert_eq!(summary.queued_requests, 0);
121
+    }
122
+
123
+    #[test]
124
+    fn auth_state_updates_summary() {
125
+        let state = AuthState::default();
126
+        state.set_state("verifying");
127
+        state.set_active_requests(2);
128
+        state.set_queued_requests(3);
129
+
130
+        let summary = state.summary();
131
+        assert_eq!(summary.state, "verifying");
132
+        assert_eq!(summary.active_requests, 2);
133
+        assert_eq!(summary.queued_requests, 3);
134
+    }
135
+}
garcardctl/Cargo.tomladded
@@ -0,0 +1,13 @@
1
+[package]
2
+name = "garcardctl"
3
+version.workspace = true
4
+edition.workspace = true
5
+license.workspace = true
6
+authors.workspace = true
7
+
8
+[dependencies]
9
+garcard-ipc.workspace = true
10
+
11
+anyhow.workspace = true
12
+clap.workspace = true
13
+serde_json.workspace = true
garcardctl/src/main.rsadded
@@ -0,0 +1,79 @@
1
+use anyhow::{Context, Result};
2
+use clap::{Parser, Subcommand};
3
+use garcard_ipc::{Command, Response, socket_path};
4
+use std::io::{BufRead, BufReader, Write};
5
+use std::os::unix::net::UnixStream;
6
+
7
+#[derive(Parser, Debug)]
8
+#[command(name = "garcardctl", about = "Control and inspect garcard daemon")]
9
+struct Cli {
10
+    #[command(subcommand)]
11
+    command: Commands,
12
+}
13
+
14
+#[derive(Subcommand, Debug)]
15
+enum Commands {
16
+    Ping,
17
+    Status,
18
+    Version,
19
+    AuthSummary,
20
+    Quit,
21
+}
22
+
23
+fn main() -> Result<()> {
24
+    let cli = Cli::parse();
25
+    let command = to_protocol_command(cli.command);
26
+    let response = send_command(&command)?;
27
+
28
+    if response.success {
29
+        if let Some(data) = response.data {
30
+            println!("{}", serde_json::to_string_pretty(&data)?);
31
+        } else {
32
+            println!("OK");
33
+        }
34
+        return Ok(());
35
+    }
36
+
37
+    let message = response
38
+        .error
39
+        .unwrap_or_else(|| "unknown daemon error".to_string());
40
+    eprintln!("Error: {}", message);
41
+    std::process::exit(1);
42
+}
43
+
44
+fn to_protocol_command(command: Commands) -> Command {
45
+    match command {
46
+        Commands::Ping => Command::Ping,
47
+        Commands::Status => Command::Status,
48
+        Commands::Version => Command::Version,
49
+        Commands::AuthSummary => Command::AuthSummary,
50
+        Commands::Quit => Command::Quit,
51
+    }
52
+}
53
+
54
+fn send_command(command: &Command) -> Result<Response> {
55
+    let socket = socket_path();
56
+    let mut stream = UnixStream::connect(&socket).with_context(|| {
57
+        format!(
58
+            "failed to connect to garcard daemon at {} (is it running?)",
59
+            socket.display()
60
+        )
61
+    })?;
62
+
63
+    let request = serde_json::to_string(command).context("failed to serialize command")?;
64
+    writeln!(stream, "{}", request).context("failed to send command")?;
65
+    stream.flush().context("failed to flush command")?;
66
+
67
+    let mut reader = BufReader::new(stream);
68
+    let mut line = String::new();
69
+    let bytes = reader
70
+        .read_line(&mut line)
71
+        .context("failed to read daemon response")?;
72
+    if bytes == 0 {
73
+        anyhow::bail!("daemon closed socket before responding");
74
+    }
75
+
76
+    let response: Response =
77
+        serde_json::from_str(line.trim()).context("failed to decode daemon response")?;
78
+    Ok(response)
79
+}