qsu

Check-in Differences
Login

Check-in Differences

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Difference From qsu-0.6.1 To qsu-0.6.2

2024-10-17
11:47
Implicitly trace log when status reporter reports service state changes. check-in: 1bfa789ca3 user: jan tags: trunk
06:11
Release maintenance. check-in: 0e755c10e7 user: jan tags: qsu-0.6.2, trunk
06:10
Crate maintenance. check-in: 7ec935e407 user: jan tags: trunk
2024-10-16
15:01
Make display_name and description options generally available for register-service subcommand. check-in: 3c799881cf user: jan tags: trunk
2024-10-05
20:32
Release maintenance. check-in: 2e398322b7 user: jan tags: qsu-0.6.1, trunk
20:25
Crate maintenance. check-in: c677092c76 user: jan tags: trunk

Changes to Cargo.toml.

1
2
3

4
5
6
7
8
9
10
1
2

3
4
5
6
7
8
9
10


-
+







[package]
name = "qsu"
version = "0.6.1"
version = "0.6.2"
edition = "2021"
license = "0BSD"
# https://crates.io/category_slugs
categories = [ "os" ]
keywords = [ "service", "systemd", "winsvc" ]
repository = "https://repos.qrnch.tech/pub/qsu"
description = "Service subsystem utilities and runtime wrapper."
34
35
36
37
38
39
40
41

42
43
44
45
46
47
48
34
35
36
37
38
39
40

41
42
43
44
45
46
47
48







-
+







rt = []
tokio = ["rt", "tokio/macros", "tokio/rt-multi-thread", "tokio/signal"]
wait-for-debugger = ["dep:dbgtools-win"]

[dependencies]
async-trait = { version = "0.1.83" }
chrono = { version = "0.4.38" }
clap = { version = "4.5.19", optional = true, features = [
clap = { version = "4.5.20", optional = true, features = [
  "derive", "env", "string", "wrap_help"
] }
env_logger = { version = "0.11.5" }
futures = { version = "0.3.31" }
itertools = { version = "0.13.0", optional = true }
killswitch = { version = "0.4.2" }
log = { version = "0.4.22" }
58
59
60
61
62
63
64
65

66
67
68
69
70
71
72
73
74
75
76
77
78
79
80

81
82
83
84
85
86
87
58
59
60
61
62
63
64

65
66
67
68
69
70
71
72
73
74
75
76
77
78
79

80
81
82
83
84
85
86
87







-
+














-
+







default-features = false
features = ["env-filter", "time", "fmt", "ansi"]

[target.'cfg(target_os = "linux")'.dependencies]
sd-notify = { version = "0.4.3", optional = true }

[target.'cfg(unix)'.dependencies]
libc = { version = "0.2.159" }
libc = { version = "0.2.160" }
nix = { version = "0.29.0", features = ["pthread", "signal", "time"] }

[target.'cfg(windows)'.dependencies]
dbgtools-win = { version = "0.2.1", optional = true }
eventlog = { version = "0.2.2" }
registry = { version = "1.2.3" }
scopeguard = { version = "1.2.0" }
windows-service = { version = "0.7.0" }
windows-sys = { version = "0.59.0", features = [
  "Win32_Foundation", "Win32_System_Console"
] }
winreg = { version = "0.52.0" }

[dev-dependencies]
clap = { version = "4.5.4", features = ["derive", "env", "wrap_help"] }
clap = { version = "4.5.20", features = ["derive", "env", "wrap_help"] }
tokio = { version = "1.40.0", features = ["time"] }

[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs", "--generate-link-to-definition"]

[[example]]

Changes to src/argp.rs.

84
85
86
87
88
89
90

91

92
93
94
95

96

97
98
99
100
101
102
103
84
85
86
87
88
89
90
91

92
93
94
95
96
97

98
99
100
101
102
103
104
105







+
-
+




+
-
+







#[derive(Debug, Args)]
struct RegSvcArgs {
  /// Autostart service at boot.
  #[arg(short = 's', long)]
  auto_start: bool,

  /// Set an optional display name for the service.
  ///
  #[cfg(windows)]
  /// Only used on Windows.
  #[arg(short = 'D', long, value_name = "DISPNAME")]
  display_name: Option<String>,

  /// Set an optional one-line service description.
  ///
  #[cfg(any(all(target_os = "linux", feature = "systemd"), windows))]
  /// Only used on Windows and Linux with systemd.
  #[arg(short, long, value_name = "DESC")]
  description: Option<String>,

  /// Add a command line argument to the service command line.
  #[arg(short, long)]
  arg: Vec<String>,

248
249
250
251
252
253
254

255


256


257


258
259
260
261
262
263
264
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273







+

+
+

+
+

+
+







  }
}


/// Used to differentiate between running without a subcommand, or the
/// install/uninstall or run service subcommands.
pub enum Cmd {
  /// The root `Command`.
  Root,

  /// The service registration/installation `Command`.
  Inst,

  /// The service deregistration/uninstallation `Command`.
  Rm,

  /// The service run `Command`.
  Run
}

/// Allow application to customise behavior of an [`ArgParser`] instance.
pub trait ArgsProc {
  type AppErr;

Changes to src/rt.rs.

67
68
69
70
71
72
73
74

75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
















90
91
92
93
94
95
96
67
68
69
70
71
72
73

74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112







-
+















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







mod systemd;

#[cfg(windows)]
pub mod winsvc;

use std::sync::{
  atomic::{AtomicU32, Ordering},
  Arc
  Arc, OnceLock
};

#[cfg(any(feature = "tokio", feature = "rocket"))]
use async_trait::async_trait;

#[cfg(feature = "tokio")]
use tokio::runtime;

use tokio::sync::broadcast;

#[cfg(all(target_os = "linux", feature = "systemd"))]
use sd_notify::NotifyState;


use crate::{err::CbErr, lumberjack::LumberJack};


#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum RunAs {
  Foreground,
  SvcSubsys
}

/// Keep track of whether the service application is running as foreground
/// process or running under a service subsystem.
static RUNAS: OnceLock<RunAs> = OnceLock::new();

pub fn runas() -> Option<RunAs> {
  RUNAS.get().copied()
}


/// The run time environment.
///
/// Can be used by the application callbacks to determine whether it is running
/// as a service.
#[derive(Debug, Clone)]
pub enum RunEnv {
143
144
145
146
147
148
149






150
151
152
153
154
155
156
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178







+
+
+
+
+
+







  fn stopping(&self, checkpoint: u32, msg: Option<StateMsg>);

  fn stopped(&self);
}


/// Context passed to `init()` service application callback.
///
/// This context can be used to query whether the service application is
/// running as foreground process or running within a service subsystem.
///
/// It can also be used to report progress status back to the service
/// subsystems, for platforms that support it.
pub struct InitCtx {
  re: RunEnv,
  sr: Arc<dyn StateReporter + Send + Sync>,
  cnt: Arc<AtomicU32>
}

impl InitCtx {
169
170
171
172
173
174
175






176
177
178
179
180
181
182
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210







+
+
+
+
+
+







    let checkpoint = self.cnt.fetch_add(1, Ordering::SeqCst);
    self.sr.starting(checkpoint, status);
  }
}


/// Context passed to `term()` service application callback.
///
/// This context can be used to query whether the service application is
/// running as foreground process or running within a service subsystem.
///
/// It can also be used to report progress status back to the service
/// subsystems, for platforms that support it.
pub struct TermCtx {
  re: RunEnv,
  sr: Arc<dyn StateReporter + Send + Sync>,
  cnt: Arc<AtomicU32>
}

impl TermCtx {
606
607
608
609
610
611
612


613
614
615
616
617
618
619
620


621
622
623
624
625
626
627
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659







+
+








+
+







  /// application callback returned an error. [`CbErr::Lib`] indicates that an
  /// error occurred in the qsu runtime.
  pub fn run<ApEr>(self, st: SrvAppRt<ApEr>) -> Result<(), CbErr<ApEr>>
  where
    ApEr: Send + 'static
  {
    if self.service {
      let _ = RUNAS.set(RunAs::SvcSubsys);

      #[cfg(all(target_os = "linux", feature = "systemd"))]
      self.systemd(st)?;

      #[cfg(windows)]
      self.winsvc(st)?;

      // ToDo: We should check for other platforms here (like macOS/launchd)
    } else {
      let _ = RUNAS.set(RunAs::Foreground);

      // Do not run against any specific service subsystem.  Despite its name
      // this isn't necessarily running as a foreground process; some service
      // subsystems do not make a distinction.  Perhaps a better mental model
      // is that certain service subsystems expects to run regular "foreground"
      // processes.
      self.foreground(st)?;
    }

Changes to www/changelog.md.

1
2
3
4
5
6
7

8
9
10
11
12
13
14


















15
16
17
18
19
20
21
1
2
3
4
5
6

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39






-
+







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







# Change log

⚠️  indicates a breaking change.

## [Unreleased]

[Details](/vdiff?from=qsu-0.6.1&to=trunk)
[Details](/vdiff?from=qsu-0.6.2&to=trunk)

### Added

### Changed

### Removed

---

## [0.6.2] - 2024-10-17

[Details](/vdiff?from=qsu-0.6.1&to=qsu-0.6.2)

### Added

- After the qsu runtime as been launched `rt::runas()` can be used to probe
  whether the runtime was run as a foreground application or as a service
  subsystem application.

### Changed

- Make `display_name` and `description` options for `register-service`
  generally available on all platforms, but document which platforms they are
  used on."

---

## [0.6.1] - 2024-10-05

[Details](/vdiff?from=qsu-0.6.0&to=qsu-0.6.1)

### Changed