qsu

Check-in Differences
Login

Check-in Differences

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

Difference From qsu-0.0.7 To qsu-0.1.0

2024-01-16
15:59
Crate maintenance: Update apperr to 0.2.0, and fix a bug that was uncovered by it. check-in: f22db69940 user: jan tags: trunk
2024-01-10
13:53
Documentation updates and prepare for 0.1.0. check-in: 54ae1190a4 user: jan tags: qsu-0.1.0, trunk
2023-12-13
13:06
Add missing comma. check-in: d9feea0cec user: jan tags: trunk
2023-12-12
05:22
Use apperr crate instead of using an in-tree implementation of AppErr. check-in: 49f2ff9578 user: jan tags: trunk
2023-12-11
11:35
Release maintenance. check-in: a01d88982c user: jan tags: qsu-0.0.7, trunk
2023-12-09
11:38
Update dependencies. Release maintenance. check-in: 13c503aedd user: jan tags: trunk

Changes to Cargo.toml.

1
2
3
4
5
6
7
8
9
10
[package]
name = "qsu"
version = "0.0.7"
edition = "2021"
license = "0BSD"
categories = [ "asynchronous" ]
keywords = [ "service", "systemd", "winsvc" ]
repository = "https://repos.qrnch.tech/pub/qsu"
description = "Service subsystem wrapper."
rust-version = "1.56"


|







1
2
3
4
5
6
7
8
9
10
[package]
name = "qsu"
version = "0.1.0"
edition = "2021"
license = "0BSD"
categories = [ "asynchronous" ]
keywords = [ "service", "systemd", "winsvc" ]
repository = "https://repos.qrnch.tech/pub/qsu"
description = "Service subsystem wrapper."
rust-version = "1.56"
26
27
28
29
30
31
32

33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
systemd = ["dep:sd-notify"]
rocket = ["rt", "dep:rocket", "tokio"]
rt = []
tokio = ["rt", "tokio/macros", "tokio/rt-multi-thread", "tokio/signal"]
wait-for-debugger = ["dep:dbgtools-win"]

[dependencies]

async-trait = { version = "0.1.74" }
chrono = { version = "0.4.24" }
clap = { version = "4.4.11", optional = true, features = [
  "derive", "env", "string", "wrap_help"
] }
env_logger = { version = "0.10.0" }
futures = { version = "0.3.29" }
itertools = { version = "0.12.0", optional = true }
killswitch = { version = "0.4.2" }
log = { version = "0.4.20" }
parking_lot = { version = "0.12.1" }
rocket = { version = "0.5.0", optional = true }
sidoc = { version = "0.1.0", optional = true }
tokio = { version = "1.35.0", features = ["sync"] }
time = { version = "0.3.20", features = ["macros"] }
tracing = { version = "0.1.40" }

[dependencies.tracing-subscriber]
version = "0.3.18"
default-features = false
features = ["env-filter", "time", "fmt", "ansi"]

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

[target.'cfg(unix)'.dependencies]
libc = { version = "0.2.149" }
nix = { version = "0.27.1", features = ["pthread", "signal"] }

[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.6.0" }
windows-sys = { version = "0.52.0", features = [
  "Win32_Foundation", "Win32_System_Console"
] }
winreg = { version = "0.52.0" }

[dev-dependencies]
clap = { version = "4.4.6", features = ["derive", "env", "wrap_help"] }
tokio = { version = "1.33.0", features = ["time"] }

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

[[example]]
name = "hellosvc"







>
|
|
|


|
|






|
|











|














|
|







26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
systemd = ["dep:sd-notify"]
rocket = ["rt", "dep:rocket", "tokio"]
rt = []
tokio = ["rt", "tokio/macros", "tokio/rt-multi-thread", "tokio/signal"]
wait-for-debugger = ["dep:dbgtools-win"]

[dependencies]
apperr = { version = "0.1.0" }
async-trait = { version = "0.1.77" }
chrono = { version = "0.4.31" }
clap = { version = "4.4.14", optional = true, features = [
  "derive", "env", "string", "wrap_help"
] }
env_logger = { version = "0.10.1" }
futures = { version = "0.3.30" }
itertools = { version = "0.12.0", optional = true }
killswitch = { version = "0.4.2" }
log = { version = "0.4.20" }
parking_lot = { version = "0.12.1" }
rocket = { version = "0.5.0", optional = true }
sidoc = { version = "0.1.0", optional = true }
tokio = { version = "1.35.1", features = ["sync"] }
time = { version = "0.3.31", features = ["macros"] }
tracing = { version = "0.1.40" }

[dependencies.tracing-subscriber]
version = "0.3.18"
default-features = false
features = ["env-filter", "time", "fmt", "ansi"]

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

[target.'cfg(unix)'.dependencies]
libc = { version = "0.2.152" }
nix = { version = "0.27.1", features = ["pthread", "signal"] }

[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.6.0" }
windows-sys = { version = "0.52.0", features = [
  "Win32_Foundation", "Win32_System_Console"
] }
winreg = { version = "0.52.0" }

[dev-dependencies]
clap = { version = "4.4.14", features = ["derive", "env", "wrap_help"] }
tokio = { version = "1.35.1", features = ["time"] }

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

[[example]]
name = "hellosvc"

Changes to src/argp.rs.

1





























2
3
4
5
6
7
8
//! Helpers for integrating clap into an application using _qsu_.






























use clap::{builder::Str, Arg, ArgAction, ArgMatches, Args, Command};

use crate::{
  err::{AppErr, Error},
  installer::{self, RegSvc},
  lumberjack::LogLevel,

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







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
//! Helpers for integrating clap into an application using _qsu_.
//!
//! The purpose of the argument parser is to allow a semi-standard command line
//! interface for registering, deregistering and allowing services to run both
//! in the foreground as well as run as actual services.  To accomplish this,
//! the argument parser wraps around the runtime.  Specifically, this means
//! that if the argument parser is used, the application does not need to
//! launch the runtime explicitly; the argument parser will do this if the
//! appropriate arguments have been passed to it.
//!
//! Assuming a service handler has been implemented already, a simple use-case
//! for the argument parser in a server application involves:
//! 1. Implement the [`ArgsProc`] trait on an application object, and implement
//!    the [`ArgsProc::build_apprt()`] method.  This method should return a
//!    [`SrvAppRt`] object, containing the service handler.
//! 2. Create an instance of the [`ArgParser`], passing in the service name and
//!    a reference to an [`ArgsProc`] object.
//! 3. Call [`ArgParser::proc()`] to process the command line arguments.
//!
//! The argument parser will determine whether the caller requested to
//! register/deregister the service, run the server application in foreground
//! mode or as a system service.
//!
//! # Customization
//! While the argument parser in _qsu_ is rather opinionated, it does allow
//! for _some_ customization.  The names of the subcommands to (de)register or
//! run the server application as a service can be modified by the
//! application.  It is possible to register custom subcommands, and the
//! command line parser specification can be modified by the [`ArgsProc`]
//! callbacks.

use clap::{builder::Str, Arg, ArgAction, ArgMatches, Args, Command};

use crate::{
  err::{AppErr, Error},
  installer::{self, RegSvc},
  lumberjack::LogLevel,
192
193
194
195
196
197
198


199
200
201
202
203
204
205
  pub fn from_cmd_match(matches: &ArgMatches) -> Self {
    let svcname = matches.get_one::<String>("svcname").unwrap().to_owned();
    Self { svcname }
  }
}




pub enum Cmd {
  Root,
  Inst,
  Rm,
  Run
}








>
>







221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
  pub fn from_cmd_match(matches: &ArgMatches) -> Self {
    let svcname = matches.get_one::<String>("svcname").unwrap().to_owned();
    Self { svcname }
  }
}


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

Changes to src/err.rs.

1


2
3
4
5
6
7
8
use std::{any::Any, fmt, io};



#[derive(Debug)]
pub enum ArgsError {
  #[cfg(feature = "clap")]
  Clap(clap::Error),
  Msg(String)
}
|
>
>







1
2
3
4
5
6
7
8
9
10
use std::{fmt, io};

pub use apperr::AppErr;

#[derive(Debug)]
pub enum ArgsError {
  #[cfg(feature = "clap")]
  Clap(clap::Error),
  Msg(String)
}
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
#[cfg(windows)]
impl From<windows_service::Error> for Error {
  fn from(err: windows_service::Error) -> Self {
    Error::SubSystem(err.to_string())
  }
}


/// An error type used to store an application-specific error.
///
/// Application call-backs return this type for the `Err()` case in order to
/// allow the errors to be passed back to the application call that started the
/// service runtime.
#[repr(transparent)]
#[derive(Debug)]
pub struct AppErr(Box<dyn Any + Send + 'static>);

impl AppErr {
  pub fn new<E>(e: E) -> Self
  where
    E: Send + 'static
  {
    Self(Box::new(e))
  }

  /// Inspect error type wrapped by the `AppErr`.
  pub fn is<T>(&self) -> bool
  where
    T: Any
  {
    self.0.is::<T>()
  }

  /// Attempt to unpack and cast the inner error type.
  ///
  /// If it can't be downcast to `E`, `AppErr` will be returned in the `Err()`
  /// case.
  ///
  /// ```
  /// use qsu::AppErr;
  ///
  /// enum MyErr {
  ///   Something(String)
  /// }
  /// let apperr = AppErr::new(MyErr::Something("hello".into()));
  ///
  /// let Ok(e) = apperr.try_into_inner::<MyErr>() else {
  ///   panic!("Unexpectedly not MyErr");
  /// };
  /// ```
  pub fn try_into_inner<E: 'static>(self) -> Result<E, AppErr> {
    match self.0.downcast::<E>() {
      Ok(e) => Ok(*e),
      Err(e) => Err(AppErr(e))
    }
  }

  /// Unwrap application-specific error from an [`Error`](crate::err::Error).
  ///
  /// ```
  /// use qsu::AppErr;
  ///
  /// enum MyErr {
  ///   Something(String)
  /// }
  /// let apperr = AppErr::new(MyErr::Something("hello".into()));
  ///
  /// let MyErr::Something(e) = apperr.unwrap_inner::<MyErr>() else {
  ///   panic!("Unexpectedly not MyErr::Something");
  /// };
  /// assert_eq!(e, "hello");
  /// ```
  ///
  /// # Panic
  /// Panics if the inner type is not castable to `E`.
  pub fn unwrap_inner<E: 'static>(self) -> E {
    let Ok(e) = self.0.downcast::<E>() else {
      panic!("Unable to downcast to error type E");
    };
    *e
  }
}

impl From<AppErr> for Error {
  /// Wrap an [`AppErr`] in an [`Error`].
  fn from(err: AppErr) -> Self {
    Error::App(err)
  }
}

// vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 :







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<








235
236
237
238
239
240
241












































































242
243
244
245
246
247
248
249
#[cfg(windows)]
impl From<windows_service::Error> for Error {
  fn from(err: windows_service::Error) -> Self {
    Error::SubSystem(err.to_string())
  }
}













































































impl From<AppErr> for Error {
  /// Wrap an [`AppErr`] in an [`Error`].
  fn from(err: AppErr) -> Self {
    Error::App(err)
  }
}

// vim: set ft=rust et sw=2 ts=2 sts=2 cinoptions=2 tw=79 :

Changes to src/lib.rs.

1
2
3
4
5
6
7
8
9
10
//! _qsu_ is a set of tools for integrating a server application against a
//! service subsystem (such as
//! [Windows Services](https://learn.microsoft.com/en-us/windows/win32/services/services) [systemd](https://systemd.io/), or
//! [launchd](https://www.launchd.info/)).
//!
//! It offers a thin runtime wrapper layer with the purpose of abstracting away
//! differences between service subsystems (and also provides the same
//! interface when running the server application as a foreground process).
//! More information about the wrapper runtime can be found in the [rt] module
//! documentation.


|







1
2
3
4
5
6
7
8
9
10
//! _qsu_ is a set of tools for integrating a server application against a
//! service subsystem (such as
//! [Windows Services](https://learn.microsoft.com/en-us/windows/win32/services/services), [systemd](https://systemd.io/), or
//! [launchd](https://www.launchd.info/)).
//!
//! It offers a thin runtime wrapper layer with the purpose of abstracting away
//! differences between service subsystems (and also provides the same
//! interface when running the server application as a foreground process).
//! More information about the wrapper runtime can be found in the [rt] module
//! documentation.

Changes to www/changelog.md.

1
2
3
4
5
6
7
8
9
10









11
12
13
14
15
16
17
# Change log

## [Unreleased]

### Added

### Changed

### Removed










---

## [0.0.7] - 2023-12-09

### Changed

- Use [rocket 0.5.0](https://rocket.rs/v0.5/news/2023-11-17-version-0.5/)










>
>
>
>
>
>
>
>
>







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

## [Unreleased]

### Added

### Changed

### Removed

---

## [0.1.0] - 2024-01-10

### Changed

- Use apperr crate for `AppErr` instead of using a custom in-tree
  implementation.

---

## [0.0.7] - 2023-12-09

### Changed

- Use [rocket 0.5.0](https://rocket.rs/v0.5/news/2023-11-17-version-0.5/)