NAME

App::FileSync::Transport::Local - run the server as a local child process

SYNOPSIS

my $transport = App::FileSync::Transport::Local->new(
	root => '/srv/filesync', repo => 'docs', client => 'laptop',
);
my $connection = App::FileSync::Protocol->new(in => $transport->reader, out => $transport->writer);
...
$transport->close;

METHODS

new

Purpose: start a server for one session.

Args: root (the server root), repo, client, optional role (default client) and server_bin (default FILESYNC_SERVER_BIN, then a bin/filesync-server found above this module, then filesync-server on the PATH).

Returns: the transport.

Side Effects: starts $^X server_bin --root ... --repo ... --client ... --role .... The child's standard error is the parent's, never the protocol pipe: sharing it would put diagnostics into the protocol stream.

Usage:

my $transport = App::FileSync::Transport::Local->new(%args);

EXAMPLE

my $t = App::FileSync::Transport::Local->new(root => $root, repo => 'docs', client => 'test-1');
print {$t->writer} ...;

API SPECIFICATION

INPUT

{
	root       => { type => 'string', min => 1 },
	repo       => { type => 'string', min => 1 },
	client     => { type => 'string', min => 1 },
	role       => { type => 'string', optional => 1 },
	server_bin => { type => 'string', optional => 1 },
}

OUTPUT

{ type => 'object', isa => 'App::FileSync::Transport::Local' }

MESSAGES

Key                 Meaning                     Resolution
transport.missing   root, repo or client absent  pass it

FORMAL SPECIFICATION

new(a) ⇒ ∃ child • argv(child) = ⟨perl, bin, --root, a.root, --repo, a.repo, --client, a.client, --role, role⟩

reader

Documented with "writer" below.

writer

Purpose: the pipes to and from the server.

Args: none.

Returns: the read handle (server's stdout) or write handle (its stdin).

Side Effects: none.

Usage:

my $in = $transport->reader;

EXAMPLE

App::FileSync::Protocol->new(in => $t->reader, out => $t->writer);

API SPECIFICATION

INPUT

[ { name => 'self', type => 'object' } ]

OUTPUT

{ type => 'object' }

MESSAGES

None.

FORMAL SPECIFICATION

reader = self.reader ; writer = self.writer

close

Purpose: end the session's process.

Args: none.

Returns: 1. Repeated calls do nothing.

Side Effects: closes both pipes (the server sees end of file) and reaps the child.

Usage:

$transport->close;

EXAMPLE

$transport->close;	# safe to call twice

API SPECIFICATION

INPUT

[ { name => 'self', type => 'object' } ]

OUTPUT

{ type => 'integer', memberof => [ 1 ] }

MESSAGES

None.

FORMAL SPECIFICATION

closed' = 1 ∧ child ¬in; processes'

error_text

Purpose: what the transport itself said, for when the protocol failed.

Args: none.

Returns: an empty string: the local server writes its diagnostics to this process's own standard error, which the user already sees.

Side Effects: none.

Usage:

my $text = $transport->error_text;

EXAMPLE

print $transport->error_text, "\n";

API SPECIFICATION

INPUT

[ { name => 'self', type => 'object' } ]

OUTPUT

{ type => 'string' }

MESSAGES

None.

FORMAL SPECIFICATION

error_text = ""

LIMITATIONS

  • The child runs with this process's privileges: a local transport cannot enforce the separation an SSH forced command gives, so anyone who can run it can write the server root directly anyway.

AUTHOR

Nigel Horne, <njh at nigelhorne.com>

LICENCE AND COPYRIGHT

Copyright 2026 Nigel Horne.

Usage is subject to the GPL2 licence terms. If you use it, please let me know.