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
[package]
name = "qsu"
version = "0.6.1"
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."


|







1
2
3
4
5
6
7
8
9
10
[package]
name = "qsu"
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
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 = [
  "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" }







|







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.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
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" }
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"] }
tokio = { version = "1.40.0", features = ["time"] }

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

[[example]]







|














|







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.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.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
#[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)]
  #[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))]
  #[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>,








>
|




>
|







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.
  ///
  /// Only used on Windows.
  #[arg(short = 'D', long, value_name = "DISPNAME")]
  display_name: Option<String>,

  /// Set an optional one-line service description.
  ///
  /// 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
  }
}


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

  Root,


  Inst,


  Rm,


  Run
}

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








>

>
>

>
>

>
>







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
mod systemd;

#[cfg(windows)]
pub mod winsvc;

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

#[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};

















/// 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 {







|















>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







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, 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
  fn stopping(&self, checkpoint: u32, msg: Option<StateMsg>);

  fn stopped(&self);
}


/// Context passed to `init()` service application callback.






pub struct InitCtx {
  re: RunEnv,
  sr: Arc<dyn StateReporter + Send + Sync>,
  cnt: Arc<AtomicU32>
}

impl InitCtx {







>
>
>
>
>
>







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
    let checkpoint = self.cnt.fetch_add(1, Ordering::SeqCst);
    self.sr.starting(checkpoint, status);
  }
}


/// Context passed to `term()` service application callback.






pub struct TermCtx {
  re: RunEnv,
  sr: Arc<dyn StateReporter + Send + Sync>,
  cnt: Arc<AtomicU32>
}

impl TermCtx {







>
>
>
>
>
>







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
  /// 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 {


      #[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 {


      // 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)?;
    }







>
>








>
>







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
# Change log

⚠️  indicates a breaking change.

## [Unreleased]

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

### Added

### Changed

### Removed



















---

## [0.6.1] - 2024-10-05

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

### Changed






|







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







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.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