systemd-transient 0.2.0

Start transient systemd services using dbus, just like systemd-run.
Documentation
# systemd-transient

Start transient systemd services using dbus, just like systemd-run.

## Usage

```rust,no_run
use systemd_transient::{ExecCommand, ServiceType, SystemdTransientManager, UnitExit, UnitProperty};

async fn example() -> Result<(), systemd_transient::Error> {
  let manager = SystemdTransientManager::session().await?;

  let unit = manager
    .start(
      "my-job", // becomes my-job.service
      [
        UnitProperty::Description("example job".into()),
        UnitProperty::Type(ServiceType::Exec),
        UnitProperty::ExecStart(vec![ExecCommand::new("/usr/bin/sleep", ["10"])]),
      ],
    )
    .await?;

  match unit.wait_for_exit().await? {
    UnitExit::Inactive => println!("exited cleanly"),
    UnitExit::Failed => println!("failed"),
  }
  Ok(())
}
```

A name without a unit type suffix becomes a service. Other transient types
keep their suffix, e.g. `start("vm.slice", [])`. Start a transient slice before
any unit names it in `Slice=`: a slice that is only referenced is created
implicitly and is not transient. Use `escape_unit_name` for user-supplied
parts of a name; in slice names `-` means nesting.

`ExecCommand::new` uses the path as `argv[0]`; build an `ExecCommand` by hand
for a different `argv[0]` or `ignore_failure`.

`start` returns once the start job has finished, i.e. when systemd considers
the unit started; for `Type=oneshot` that is after the command has run.
Dropping or releasing the handle does not stop the unit.

`start` and `stop` have no client-side timeout. systemd applies the unit's
`TimeoutStartUSec`, `TimeoutStopUSec` and `JobTimeoutUSec` and fails the job
with `JobWaitFailed { reason: "timeout", .. }`. A `Type=oneshot` unit has no
start timeout unless you set one. If you wrap `start` in your own timeout and
drop the future, the job keeps running and you lose the handle.

`start` reuses the name of a unit that is loaded but failed or inactive, and
fails with `UnitAlreadyRunning` if the unit is running.

The handle starts the unit with `AddRef=true`, so systemd keeps it loaded after
it exits and `wait_for_exit` and `stop` can report its final state at any
time. Release the reference with `handle.release().await` or by dropping the
handle (released in a background task). Until then, `start` with the same
name fails with `UnitStillLoaded`. With `Restart=` and the default
`RestartMode=normal`, the unit passes through `failed` or `inactive` between
runs, so `wait_for_exit` returns after the first run.

`stop` only stops the run the handle started. If someone else started the
unit again (`systemctl start`), it returns `UnitRestarted` and leaves the new
run alone.

Properties not covered by `UnitProperty` can be passed as
`UnitProperty::Custom(name, value)`.

### Reusing a name

`ReusableTransientUnit` (from `manager.new_reusable(name, props)`) starts the
same unit again and again. `status()` reads the last run's state from the
exit watch without a D-Bus call, `release()` drops the reference without
stopping the unit, and `stop()` stops it and releases it.

### Taking over units

A handle's reference ends with its bus connection, so units outlive the
process that started them. `manager.adopt(name)`, or
`ReusableTransientUnit::adopt()`, takes over such a unit with a new reference
and reports its exit as `start` would. A failed unit stays loaded until it is
reset and can be adopted later; a unit that exited cleanly is collected once
nothing references it, and `adopt` then returns `None`.

`manager.list_units(&["prefix-*"])` lists loaded units by pattern, e.g. to
find units no handle claims, and `manager.stop_unit(name)` stops any unit by
its full name.

## License

Licensed under either of

* Apache License, Version 2.0
  ([LICENSE-APACHE]LICENSE-APACHE or <http://www.apache.org/licenses/LICENSE-2.0>)
* MIT license
  ([LICENSE-MIT]LICENSE-MIT or <http://opensource.org/licenses/MIT>)

at your option.

## Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
dual licensed as above, without any additional terms or conditions.