Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@ schemars = "1"
# macOS memory probing, and signalling the process group a WBPP run starts.
libc = "0.2"
[target.'cfg(target_os = "windows")'.dependencies]
# The worker as a Windows service: a thin wrapper of the Service Control
# Manager API.
windows-service = "0.8"
windows-sys = { version = "0.61", features = [
"Win32_Foundation",
"Win32_Storage_FileSystem",
Expand Down
78 changes: 78 additions & 0 deletions docs/REMOTE_WORKERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,84 @@ again every 30 seconds; `journalctl -u psf-guard-worker` shows why.
`PSF_GUARD_WORKER_EXTRA_ARGS` is split on spaces, so it cannot hold a path
that contains one.

## Run a worker as a Windows service

Use a Windows service when the worker needs to run before anyone logs in.
The service starts with Windows. It uses its own copies of the app and
settings, separate from the desktop worker.

1. Open **Settings › Workers › Worker on this computer** in the desktop
app and enter the worker settings. Turn off **Start worker at login**,
then click **Save worker settings**. If the desktop worker is running,
click **Stop worker** before installing the service.
2. Open PowerShell as administrator and run:

```powershell
psf-guard-cli worker-service install
```

To use a different settings file, add `--config FILE`.

The command installs and starts the **PSF Guard Worker** service. It copies
the executable to `%ProgramFiles%\PSF Guard Worker` and settings to
`%ProgramData%\PSF Guard Worker\worker.json`. SYSTEM, Administrators and
the service account can access the service's data folder. The settings
include the worker token.

The service runs under its own account, `NT SERVICE\PSFGuardWorker`.
Task files use `%ProgramData%\PSF Guard Worker\tasks`; the desktop app's
**Temporary task folder** setting does not change the service's task folder.

### Apply settings or app updates to the service

After changing worker settings, click **Save worker settings**. After
saving settings or updating PSF Guard, open PowerShell as administrator and
run `psf-guard-cli worker-service install` again. The command stops the
service, updates its copies of the app and settings, and restarts it.

**Restart worker** controls the desktop worker. It does not restart or
update the Windows service. While the service is running or changing state,
**Start worker** is disabled to prevent a second worker from starting.

### Check service logs and requirements

- **Logs:** filenames start with `worker` in
`%ProgramData%\PSF Guard Worker\logs`.
- **Access denied:** if the service cannot read `worker.json`, update PSF Guard
and run `psf-guard-cli worker-service install` from PowerShell opened as
administrator. This repairs the service folder's permissions and starts
the worker.
- **Failures:** if the server refuses the token, the service logs the reason
and stops. Windows restarts it after 30 seconds. Replace a refused token
in the desktop settings and run the install command again.
- **Stopping:** stop the service in Windows Services. The worker disconnects
and the server queues unfinished tasks again. Windows shutdown also gives
the worker time to disconnect.
- **Sleep:** prevent the computer from sleeping while you need it to process
tasks. A worker cannot process tasks while Windows is asleep.
- **RC-Astro tools:** the service uses rc-astro from its account's `PATH`,
or the executable specified in its settings. An installation or license
available only to your desktop account may be unavailable to the service.
If the service cannot run RC-Astro, it can still accept stack builds.
Use the desktop worker if RC-Astro requires your desktop account.
- **Shared storage:** the default service account accesses network shares
through the computer's network identity. If a share requires a user
account, change the service account in Windows Services to a user with
access to that share. Give that user modify access to
`%ProgramData%\PSF Guard Worker`. Enter folder mappings with UNC paths,
such as `\\nas\astro`, that the service account can access.
Reinstalling restores the default service account. After reinstalling,
stop the service, reapply your custom account, and start the service in
Windows Services.

### Remove the Windows service

Before uninstalling PSF Guard, open PowerShell as administrator and run
`psf-guard-cli worker-service uninstall`. This stops and removes the service
and deletes its copied executable, settings including the token, task files,
and logs. The server queues unfinished tasks again. The desktop app's
saved worker settings remain in its own settings folder.

## Choose the work a worker accepts

A worker accepts everything it can run unless you list its work with
Expand Down
21 changes: 21 additions & 0 deletions docs/design/remote-workers.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,27 @@ and registers no plugins.
- **Desktop app.** `src/desktop_worker.rs` has the commands behind
**Settings › Workers › Worker on this computer**. They read and save settings,
start the tray process, and pause, resume and stop it through the control port.
- **Windows service** (`commands/worker/service.rs`). Service startup uses
copies that unprivileged processes cannot modify. `worker-service install`
copies the executable to `%ProgramFiles%\PSF Guard Worker\psf-guard-worker.exe`
and settings to `%ProgramData%\PSF Guard Worker\worker.json`. It sets
`work_dir` to the service's own `tasks` folder. The service runs as the
virtual account `NT SERVICE\PSFGuardWorker`.
`icacls` restricts the data folder to SYSTEM, Administrators and that
account. Settings and task files inherit the folder's permissions;
reinstalling repairs permissions left by earlier installs.
Running install again stops the service, updates its copies and
configuration, and restarts it. Installation and removal wait for the
stopped service's process to exit before replacing or deleting its
executable. Startup is automatic and delayed. During preshutdown, Windows
allows up to 60 seconds for the worker to disconnect; the service reports
`StopPending` with an increasing checkpoint every five seconds.
A failed run logs its reason and exits with a service-specific code;
Windows restarts it after 30 seconds. `worker-service uninstall` stops
and removes the service, then deletes its executable, settings, task files
and logs. The desktop app disables **Start worker** until the service
stops. A Windows service state does not prove a connection to the
processing server, so the UI reports it separately.
- **Start worker at login** (`commands/worker/login.rs`): a LaunchAgent with
`RunAtLoad` on macOS, and a value under
`HKCU\Software\Microsoft\Windows\CurrentVersion\Run` on Windows. Both
Expand Down
12 changes: 12 additions & 0 deletions docs/releases/unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@
- FITS downloads in stack processing use compact controls with a download icon
and explicit labels. Long labels wrap to fit narrow panels.

- Windows worker services can now read their saved settings and start
successfully. Reinstalling the service also repairs permissions left by an
earlier installation and waits for the stopped worker process before
replacing its executable.

- A fresh flat that contributes to a light's master now provides coverage even
when older flats in the same master move the median capture time beyond
the **Flat age limit**. Age checks and the calibration report use the nearest
Expand All @@ -38,6 +43,13 @@
you log in. Opening PSF Guard while its window is open brings that window
forward instead of starting a second copy.

- Run a worker as a Windows service to process tasks before anyone logs in.
Open **Settings › Workers › Worker on this computer**, click **Save worker
settings**, then run `psf-guard-cli worker-service install` in PowerShell
opened as administrator. Run the command again after changing settings or
updating PSF Guard to update and restart the service. Desktop worker
controls do not control the service.

- Run a worker as a Linux service. The Fedora RPM and the Debian package
install a `psf-guard-worker` systemd service, not enabled. Enter the
server's address and a worker token in `/etc/psf-guard/worker.conf`, then
Expand Down
35 changes: 34 additions & 1 deletion src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,27 @@ pub struct Cli {
pub command: Commands,
}

/// CLI spelling of [`crate::server::stack_preview::snr::StackFrameOrder`].
/// What `psf-guard worker-service` does.
#[derive(Subcommand, Debug)]
pub enum WorkerServiceAction {
/// Install the service, and start it.
Install {
/// The worker's settings file (defaults to the one the desktop app
/// saves for this user).
#[arg(long, value_name = "FILE")]
config: Option<String>,
},
/// Stop and remove the service. Delete its copied executable, settings,
/// task files and logs.
Uninstall,
/// Run as the service. Windows starts this; it is not for a terminal.
#[command(hide = true)]
Run {
#[arg(long, value_name = "FILE")]
config: String,
},
}

/// How a worker reaches task files.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, ValueEnum)]
pub enum WorkerFilesArg {
Expand All @@ -25,6 +45,7 @@ pub enum WorkerFilesArg {
Shared,
}

/// CLI spelling of [`crate::server::stack_preview::snr::StackFrameOrder`].
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, ValueEnum)]
pub enum StackOrderArg {
/// Chronological.
Expand Down Expand Up @@ -1388,6 +1409,18 @@ pub enum Commands {
slots: usize,
},

/// Run the worker as a Windows service, for a computer that runs work
/// while nobody is logged in.
///
/// Save the worker's settings in the desktop app's Settings › Workers ›
/// This computer first, or write a settings file. Then, from a terminal opened
/// as administrator, run `psf-guard-cli worker-service install`. The
/// service runs as Local System and starts with Windows.
WorkerService {
#[command(subcommand)]
action: WorkerServiceAction,
},

/// Start the web server for API access and static file serving
Server {
/// Path to TOML configuration file
Expand Down
23 changes: 23 additions & 0 deletions src/cli_main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1639,6 +1639,29 @@ pub fn main() -> Result<()> {
))?;
}
}
Commands::WorkerService { action } => {
use crate::cli::WorkerServiceAction;
use crate::commands::worker::{service, WorkerSettings};
match action {
WorkerServiceAction::Install { config } => {
let path = match config {
Some(config) => std::path::PathBuf::from(config),
None => WorkerSettings::default_path()?,
};
service::install(&path)?;
println!(
"The PSF Guard Worker service runs with a copy of the settings in {}. \
After you change them, or update PSF Guard, run this command again.",
path.display()
);
}
WorkerServiceAction::Uninstall => {
service::uninstall()?;
println!("Stopped and removed the PSF Guard Worker service");
}
WorkerServiceAction::Run { config } => service::run(config.into())?,
}
}
Commands::Users { action } => {
crate::commands::users::manage_users(action)?;
}
Expand Down
1 change: 1 addition & 0 deletions src/commands/worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ use crate::remote_work::{file_digest, map_path, LocalTools, WorkChoice};

pub mod control;
pub mod login;
pub mod service;
pub mod settings;

pub use control::{WorkerControl, WorkerStatus};
Expand Down
Loading
Loading