Replace mid-sentence dashes in comments with commas/semicolons

Swapped " - " for a semicolon where it joined two independent clauses,
or a comma where the following text was an appositive/dependent phrase
with no subject of its own. Markdown-style list bullets in
tests/live_test.sh's header (leading "- Item") are unaffected - those
are structural, not sentence punctuation.

Also dropped a "Spec §5.4:" citation in edit.rs missed by the earlier
spec-citation cleanup (case-sensitive grep at the time didn't match the
capitalized "Spec").

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-14 12:36:59 +02:00
parent 40437bf78c
commit 6e6b5c1393
11 changed files with 29 additions and 29 deletions

View File

@@ -1,7 +1,7 @@
//! Atomic file writes: write to a sibling temp file, then `rename` over the //! Atomic file writes: write to a sibling temp file, then `rename` over the
//! target. Matters here specifically for the instance JSON file, which the //! target. Matters here specifically for the instance JSON file, which the
//! supervisor rewrites on every state change while it may be alive for //! supervisor rewrites on every state change while it may be alive for
//! months - a reader (`status`/`list`) must never observe a half-written //! months; a reader (`status`/`list`) must never observe a half-written
//! file, and a crash mid-write must never corrupt the last-known-good state. //! file, and a crash mid-write must never corrupt the last-known-good state.
use std::io::Write; use std::io::Write;

View File

@@ -48,12 +48,12 @@ pub enum Commands {
Completions { shell: Shell }, Completions { shell: Shell },
/// Internal: runs the supervisor loop for one profile. Not for direct /// Internal: runs the supervisor loop for one profile. Not for direct
/// use - `open` spawns this itself. /// use; `open` spawns this itself.
#[command(hide = true, name = "__supervise")] #[command(hide = true, name = "__supervise")]
Supervise { name: String }, Supervise { name: String },
} }
/// Shared mapping/connection flags for `add` and `edit` - kept as one /// Shared mapping/connection flags for `add` and `edit`, kept as one
/// struct (`#[command(flatten)]`ed into both) so the two can never drift. /// struct (`#[command(flatten)]`ed into both) so the two can never drift.
#[derive(Args, Default)] #[derive(Args, Default)]
pub struct MappingArgs { pub struct MappingArgs {

View File

@@ -18,7 +18,7 @@ pub fn run(args: CloseArgs) -> Result<()> {
} }
/// Stops `name`'s supervisor if one is actually running, and clears any /// Stops `name`'s supervisor if one is actually running, and clears any
/// stale instance file either way - shared with `remove` and `wipe`. /// stale instance file either way, shared with `remove` and `wipe`.
/// Returns whether anything was actually running. /// Returns whether anything was actually running.
pub fn close_instance(name: &str, force: bool) -> Result<bool> { pub fn close_instance(name: &str, force: bool) -> Result<bool> {
let Some(pid) = instance::running_pid(name)? else { let Some(pid) = instance::running_pid(name)? else {
@@ -28,7 +28,7 @@ pub fn close_instance(name: &str, force: bool) -> Result<bool> {
if force { if force {
// SIGKILL the whole process group (the supervisor is its own // SIGKILL the whole process group (the supervisor is its own
// group leader via setsid), not just the supervisor pid - a plain // group leader via setsid), not just the supervisor pid; a plain
// single-pid SIGKILL would leave `ssh` orphaned. // single-pid SIGKILL would leave `ssh` orphaned.
unsafe { libc::kill(-pid, libc::SIGKILL) }; unsafe { libc::kill(-pid, libc::SIGKILL) };
} else { } else {

View File

@@ -19,7 +19,7 @@ pub fn run(args: EditArgs) -> Result<()> {
p.apply_edits(&edits)?; p.apply_edits(&edits)?;
profile::save(&p)?; profile::save(&p)?;
// Spec §5.4: edit never restarts a running instance - just warn. // edit never restarts a running instance, just warn.
if instance::running_pid(&name)?.is_some() { if instance::running_pid(&name)?.is_some() {
ui::warn(&format!( ui::warn(&format!(
"'{name}' is currently open; this change won't take effect until the next open/close cycle." "'{name}' is currently open; this change won't take effect until the next open/close cycle."

View File

@@ -34,7 +34,7 @@ pub fn edits_from_mapping(m: &MappingArgs) -> ProfileEdits {
} }
} }
/// `true` if `MappingArgs` carries no edits at all - used by `edit` to /// `true` if `MappingArgs` carries no edits at all, used by `edit` to
/// reject a no-op invocation. /// reject a no-op invocation.
pub fn mapping_is_empty(m: &MappingArgs) -> bool { pub fn mapping_is_empty(m: &MappingArgs) -> bool {
m.local.is_none() m.local.is_none()

View File

@@ -24,7 +24,7 @@ pub fn run(args: OpenArgs) -> Result<()> {
open_one(&name, args.foreground, args.once) open_one(&name, args.foreground, args.once)
} }
/// Opens every `reconnect: true` profile that isn't already running - the /// Opens every `reconnect: true` profile that isn't already running, the
/// hook external autostart mechanisms are meant to call. Per-profile /// hook external autostart mechanisms are meant to call. Per-profile
/// failures are warnings, not a whole-batch failure. /// failures are warnings, not a whole-batch failure.
fn open_all(once: bool) -> Result<()> { fn open_all(once: bool) -> Result<()> {
@@ -57,7 +57,7 @@ fn open_one(name: &str, foreground: bool, once: bool) -> Result<()> {
return Ok(()); return Ok(());
} }
// Clear a stale instance file left by a crash before spawning. The // Clear a stale instance file left by a crash before spawning. The
// lock, not this file, is the authority on "already open" - this just // lock, not this file, is the authority on "already open"; this just
// keeps `status` from reading stale state mid-spawn. // keeps `status` from reading stale state mid-spawn.
instance::delete(name)?; instance::delete(name)?;

View File

@@ -10,7 +10,7 @@ pub fn run(args: StatusArgs) -> Result<()> {
let p = profile::load(&name)?; let p = profile::load(&name)?;
let inst = instance::load(&name)?; let inst = instance::load(&name)?;
// An instance file whose pid isn't actually alive means the supervisor // An instance file whose pid isn't actually alive means the supervisor
// crashed without cleaning up - report that, rather than trusting a // crashed without cleaning up; report that, rather than trusting a
// state the process table disagrees with. // state the process table disagrees with.
let live = inst.as_ref().is_some_and(|i| instance::supervisor_alive(i.pid, &name)); let live = inst.as_ref().is_some_and(|i| instance::supervisor_alive(i.pid, &name));

View File

@@ -1,4 +1,4 @@
//! Bulk profile backup/restore - a flat TOML array of the same `Profile` //! Bulk profile backup/restore, a flat TOML array of the same `Profile`
//! records `profile::save`/`load` already read and write, so it round-trips //! records `profile::save`/`load` already read and write, so it round-trips
//! through the exact same serialization with nothing profile-specific here. //! through the exact same serialization with nothing profile-specific here.
@@ -78,7 +78,7 @@ fn import(path: &str, name: Option<&str>) -> Result<()> {
Ok(()) Ok(())
} }
/// Identity files are never included in the export, only the path - warn so /// Identity files are never included in the export, only the path; warn so
/// that doesn't come as a surprise on the importing end. /// that doesn't come as a surprise on the importing end.
fn warn_about_identities(profiles: &[Profile]) { fn warn_about_identities(profiles: &[Profile]) {
let names: Vec<&str> = profiles.iter().filter(|p| p.identity.is_some()).map(|p| p.name.as_str()).collect(); let names: Vec<&str> = profiles.iter().filter(|p| p.identity.is_some()).map(|p| p.name.as_str()).collect();
@@ -91,7 +91,7 @@ fn warn_about_identities(profiles: &[Profile]) {
} }
/// After import, flag any profile whose identity path doesn't resolve on /// After import, flag any profile whose identity path doesn't resolve on
/// this machine - the most likely sign of a not-yet-copied key file. /// this machine, the most likely sign of a not-yet-copied key file.
fn warn_about_missing_identities(profiles: &[Profile]) { fn warn_about_missing_identities(profiles: &[Profile]) {
for p in profiles { for p in profiles {
if let Some(identity) = &p.identity { if let Some(identity) = &p.identity {
@@ -107,7 +107,7 @@ fn warn_about_missing_identities(profiles: &[Profile]) {
} }
/// Expands a leading `~/` the same way `ssh` itself does at spawn time /// Expands a leading `~/` the same way `ssh` itself does at spawn time
/// (`ssh.rs`) - without this, a valid `~/...` identity path would be /// (`ssh.rs`); without this, a valid `~/...` identity path would be
/// misreported as missing since `Path::is_file` never expands `~` on its own. /// misreported as missing since `Path::is_file` never expands `~` on its own.
fn expand_home(path: &str) -> PathBuf { fn expand_home(path: &str) -> PathBuf {
match path.strip_prefix("~/").zip(dirs::home_dir()) { match path.strip_prefix("~/").zip(dirs::home_dir()) {

View File

@@ -111,7 +111,7 @@ pub fn instance_path(name: &str) -> PathBuf { state_dir().join(format!("{name}.j
pub fn lock_path(name: &str) -> PathBuf { state_dir().join(format!("{name}.lock")) } pub fn lock_path(name: &str) -> PathBuf { state_dir().join(format!("{name}.lock")) }
pub fn log_path(name: &str) -> PathBuf { state_dir().join(format!("{name}.log")) } pub fn log_path(name: &str) -> PathBuf { state_dir().join(format!("{name}.log")) }
/// Loads the instance file for `name`, if any. `None` means closed - its /// Loads the instance file for `name`, if any. `None` means closed; its
/// absence *is* the closed state; there is no separate enum value for it. /// absence *is* the closed state; there is no separate enum value for it.
pub fn load(name: &str) -> Result<Option<Instance>> pub fn load(name: &str) -> Result<Option<Instance>>
{ {
@@ -146,7 +146,7 @@ pub fn delete(name: &str) -> Result<()>
pub fn process_alive(pid: i32) -> bool { unsafe { libc::kill(pid, 0) == 0 } } pub fn process_alive(pid: i32) -> bool { unsafe { libc::kill(pid, 0) == 0 } }
/// True only if `pid` is a live process whose cmdline identifies it as the /// True only if `pid` is a live process whose cmdline identifies it as the
/// supervisor for `name` - guards against a stale or reused pid. /// supervisor for `name`, guards against a stale or reused pid.
pub fn supervisor_alive(pid: i32, name: &str) -> bool pub fn supervisor_alive(pid: i32, name: &str) -> bool
{ {
if !process_alive(pid) { return false }; if !process_alive(pid) { return false };
@@ -159,7 +159,7 @@ pub fn supervisor_alive(pid: i32, name: &str) -> bool
/// The live supervisor pid for `name`, if one is currently running /// The live supervisor pid for `name`, if one is currently running
/// (checked against the process table, not just the instance file's /// (checked against the process table, not just the instance file's
/// last-known value - so a crash or reboot is detected as "not running" /// last-known value, so a crash or reboot is detected as "not running"
/// rather than trusting stale on-disk state). /// rather than trusting stale on-disk state).
pub fn running_pid(name: &str) -> Result<Option<i32>> pub fn running_pid(name: &str) -> Result<Option<i32>>
{ {

View File

@@ -53,8 +53,8 @@ fn main()
} }
} }
/// True for a bare `porthole` invocation, or top-level `-h`/`--help`/`help` /// True for a bare `porthole` invocation, or top-level `-h`/`--help`/`help`,
/// - i.e. anything that should show the expanded help rather than being /// i.e. anything that should show the expanded help rather than being
/// handled (or rejected) by a specific subcommand. /// handled (or rejected) by a specific subcommand.
fn wants_top_level_help(args: &[String]) -> bool { fn wants_top_level_help(args: &[String]) -> bool {
match args.get(1..) { match args.get(1..) {

View File

@@ -5,7 +5,7 @@
# auth (success and failure), a real two-hop ProxyJump, a real SOCKS proxy # auth (success and failure), a real two-hop ProxyJump, a real SOCKS proxy
# carrying real traffic. Deliberately NOT part of `cargo test` (same reason # carrying real traffic. Deliberately NOT part of `cargo test` (same reason
# as vmic's tests/live_test.sh: this needs a real remote server, not a CI # as vmic's tests/live_test.sh: this needs a real remote server, not a CI
# sandbox) - live-only, opt-in, run by hand. # sandbox), live-only, opt-in, run by hand.
# #
# Usage: # Usage:
# tests/live_test.sh [options] # tests/live_test.sh [options]
@@ -22,21 +22,21 @@
# -h, --help # -h, --help
# #
# Known limitation: only one real server is available, so multi-hop (`-J`) # Known limitation: only one real server is available, so multi-hop (`-J`)
# is tested by chaining the server through itself (--via user@host,user@host) # is tested by chaining the server through itself (--via user@host,user@host),
# - a real two-hop ProxyJump handshake, just with both hops the same box. # a real two-hop ProxyJump handshake, just with both hops the same box.
# There is no way to test a genuine distinct-host chain without a second # There is no way to test a genuine distinct-host chain without a second
# server. # server.
# #
# Safety: # Safety:
# - Profiles/instances/locks/logs are sandboxed for the whole run under # - Profiles/instances/locks/logs are sandboxed for the whole run under
# one `PORTHOLE_STATE_DIR_OVERRIDE` temp dir (profile.rs/instance.rs # one `PORTHOLE_STATE_DIR_OVERRIDE` temp dir (profile.rs/instance.rs
# both honor it) - this suite NEVER touches the real # both honor it); this suite NEVER touches the real
# ~/.config/porthole or ~/.local/state/porthole. # ~/.config/porthole or ~/.local/state/porthole.
# - Every profile created is named "$NAME_..." (default prefix # - Every profile created is named "$NAME_..." (default prefix
# porttestsuite); no operation targets anything outside that prefix. # porttestsuite); no operation targets anything outside that prefix.
# - `wipe` (src/commands/wipe.rs) kills ANY process on the # - `wipe` (src/commands/wipe.rs) kills ANY process on the
# whole system whose cmdline contains "__supervise", regardless of # whole system whose cmdline contains "__supervise", regardless of
# which state dir it belongs to - it is NOT scoped by the sandboxing # which state dir it belongs to; it is NOT scoped by the sandboxing
# above. Before running it, this script scans the real process table # above. Before running it, this script scans the real process table
# and skips the wipe phase entirely (not "wipe only the safe parts") # and skips the wipe phase entirely (not "wipe only the safe parts")
# if it finds a live __supervise process that isn't one of this run's # if it finds a live __supervise process that isn't one of this run's
@@ -48,10 +48,10 @@
# never writes to your real ~/.ssh/known_hosts (only porthole's own # never writes to your real ~/.ssh/known_hosts (only porthole's own
# spawned ssh does, against the real file, using the accept-new policy # spawned ssh does, against the real file, using the accept-new policy
# already forced in src/ssh.rs). # already forced in src/ssh.rs).
# - An EXIT trap always attempts full cleanup - closes/removes every # - An EXIT trap always attempts full cleanup, closes/removes every
# test-prefixed profile in every sandbox dir used, force-kills any # test-prefixed profile in every sandbox dir used, force-kills any
# stray matching __supervise process, deletes the throwaway bad-auth # stray matching __supervise process, deletes the throwaway bad-auth
# key - even on failure or Ctrl-C. # key, even on failure or Ctrl-C.
set -uo pipefail set -uo pipefail
cd "$(dirname "${BASH_SOURCE[0]}")/.." cd "$(dirname "${BASH_SOURCE[0]}")/.."
@@ -117,7 +117,7 @@ p_run() { LAST_OUT="$(PORTHOLE_STATE_DIR_OVERRIDE="$1" "$BIN" "${@:2}" 2>&1)"; L
# status --json's "state" field is always one of closed/up/reconnecting/error # status --json's "state" field is always one of closed/up/reconnecting/error
# (print_json normalizes a dead-supervisor instance file to "error" too, see # (print_json normalizes a dead-supervisor instance file to "error" too, see
# status.rs) - polling that key is far more robust than scraping the padded # status.rs); polling that key is far more robust than scraping the padded
# human-readable field. # human-readable field.
json_field() { # json_field <state-dir> <name> <field> -> value, empty if absent/no instance json_field() { # json_field <state-dir> <name> <field> -> value, empty if absent/no instance
p "$1" status "$2" --json 2>/dev/null | sed -n "s/.*\"$3\": \"\\{0,1\\}\\([^\",]*\\)\"\\{0,1\\},\\{0,1\\}\$/\\1/p" | head -1 p "$1" status "$2" --json 2>/dev/null | sed -n "s/.*\"$3\": \"\\{0,1\\}\\([^\",]*\\)\"\\{0,1\\},\\{0,1\\}\$/\\1/p" | head -1
@@ -140,7 +140,7 @@ wait_port_closed() { # wait_port_closed <port> [tries, x0.5s]
} }
SSH_PROBE_OPTS=(-o BatchMode=yes -o ConnectTimeout=5 -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=/dev/null) SSH_PROBE_OPTS=(-o BatchMode=yes -o ConnectTimeout=5 -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=/dev/null)
# mktemp's output is captured via $(...), which forks a subshell - any # mktemp's output is captured via $(...), which forks a subshell; any
# array append done *inside* a function called that way would be lost when # array append done *inside* a function called that way would be lost when
# the subshell exits, so state dirs are appended here at the call site # the subshell exits, so state dirs are appended here at the call site
# instead of through a helper function. # instead of through a helper function.
@@ -408,7 +408,7 @@ expect_contains "warns it's left running untracked" "left running untracked"
p_run "$STATE_DIR" status "${NAME}_keep"; assert_eq "profile is gone from tracking" "$LAST_CODE" "1" p_run "$STATE_DIR" status "${NAME}_keep"; assert_eq "profile is gone from tracking" "$LAST_CODE" "1"
assert_true "the untracked process is still actually alive" pgrep -f "__supervise ${NAME}_keep\$" assert_true "the untracked process is still actually alive" pgrep -f "__supervise ${NAME}_keep\$"
assert_true "port $KEEP_PORT is still live, untracked" port_open "$KEEP_PORT" assert_true "port $KEEP_PORT is still live, untracked" port_open "$KEEP_PORT"
# left running on purpose - phase 15's wipe is what's being tested against it # left running on purpose; phase 15's wipe is what's being tested against it
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
section "Phase 14: transfer round trip against a live profile" section "Phase 14: transfer round trip against a live profile"