Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable server use as a library #11

Merged
merged 1 commit into from
Mar 27, 2022
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
11 changes: 3 additions & 8 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"path/filepath"

"github.com/BurntSushi/toml"
"github.com/gokrazy/rsync/rsyncd"
)

type Listener struct {
Expand All @@ -14,15 +15,9 @@ type Listener struct {
AnonSSH string `toml:"anon_ssh"`
}

type Module struct {
Name string `toml:"name"`
Path string `toml:"path"`
ACL []string `toml:"acl"`
}

type Config struct {
Listeners []Listener `toml:"listener"`
Modules []Module `toml:"module"`
Listeners []Listener `toml:"listener"`
Modules []rsyncd.Module `toml:"module"`
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As per our discussion, no need to expose this config package if Module is defined by rsyncd package itself.

Also related: we discussed earlier renaming config -> rsyncdconfig. I reverted the rename and left it out from this PR, since the package is not needed as public.

If you want, I can send another PR renaming internal/config -> internal/rsyncdconfig. But it wasn't necessary here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you have time for the internal/configinternal/rsyncdconfig change as well, I’ll gladly take that as a separate pull request later :)

}

func FromString(input string) (*Config, error) {
Expand Down
3 changes: 2 additions & 1 deletion internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"testing"

"github.com/gokrazy/rsync/internal/config"
"github.com/gokrazy/rsync/rsyncd"
"github.com/google/go-cmp/cmp"
)

Expand Down Expand Up @@ -43,7 +44,7 @@ path = "/non/existant/path"
}

{
want := []config.Module{
want := []rsyncd.Module{
{Name: "interop", Path: "/non/existant/path"},
}
if diff := cmp.Diff(want, cfg.Modules); diff != "" {
Expand Down
22 changes: 16 additions & 6 deletions internal/maincmd/maincmd.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
// Package maincmd implements a subset of the '$ rsync' CLI surface, namely that it can:
// - serve as a server daemon over TCP or SSH (via SSH session stdin/stdout)
// - act as "client" CLI for connecting to the server
// - Not yet implemented: both "client" and "server" can act as the sender and the receiver
package maincmd

import (
Expand All @@ -12,7 +16,7 @@ import (

"github.com/gokrazy/rsync/internal/anonssh"
"github.com/gokrazy/rsync/internal/config"
"github.com/gokrazy/rsync/internal/rsyncd"
"github.com/gokrazy/rsync/rsyncd"

// For profiling and debugging
_ "net/http/pprof"
Expand Down Expand Up @@ -53,7 +57,10 @@ func Main(ctx context.Context, args []string, stdin io.Reader, stdout io.Writer,
return err
}
}
srv := rsyncd.NewServer(cfg.Modules...)
srv, err := rsyncd.NewServer(cfg.Modules)
if err != nil {
return err
}
rw := readWriter{
r: stdin,
w: stdout,
Expand All @@ -66,7 +73,7 @@ func Main(ctx context.Context, args []string, stdin io.Reader, stdout io.Writer,
if opts.Server {
// start_server()
srv := &rsyncd.Server{}
mod := config.Module{
mod := rsyncd.Module{
Name: "implicit",
Path: "/",
}
Expand Down Expand Up @@ -112,7 +119,7 @@ func Main(ctx context.Context, args []string, stdin io.Reader, stdout io.Writer,
AnonSSH: opts.Gokrazy.AnonSSHListen,
},
},
Modules: []config.Module{},
Modules: []rsyncd.Module{},
}
} else {
return cfgErr
Expand Down Expand Up @@ -158,7 +165,7 @@ func Main(ctx context.Context, args []string, stdin io.Reader, stdout io.Writer,
if len(parts) != 2 {
return fmt.Errorf("malformed -gokr.modulemap parameter %q, expected <modulename>=<path>", moduleMap)
}
module := config.Module{
module := rsyncd.Module{
Name: parts[0],
Path: parts[1],
}
Expand Down Expand Up @@ -187,7 +194,10 @@ func Main(ctx context.Context, args []string, stdin io.Reader, stdout io.Writer,
}()
}

srv := rsyncd.NewServer(cfg.Modules...)
srv, err := rsyncd.NewServer(cfg.Modules)
if err != nil {
return err
}
var ln net.Listener
listeners, err := systemdListeners()
if err != nil {
Expand Down
4 changes: 2 additions & 2 deletions internal/maincmd/namespacing.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ import (
"os/exec"
"strconv"

"github.com/gokrazy/rsync/internal/config"
"github.com/gokrazy/rsync/rsyncd"
)

func namespace(modules []config.Module, listen string) error {
func namespace(modules []rsyncd.Module, listen string) error {
if os.Getenv("GOKRAZY_RSYNC_PRIVDROP") != "" {
log.Printf("pid %d (privileges dropped)", os.Getpid())

Expand Down
4 changes: 2 additions & 2 deletions internal/maincmd/namespacing_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
"strconv"
"syscall"

"github.com/gokrazy/rsync/internal/config"
"github.com/gokrazy/rsync/rsyncd"
"golang.org/x/sys/unix"
)

Expand Down Expand Up @@ -71,7 +71,7 @@ func pivotRoot(newroot string) error {
return nil
}

func namespace(modules []config.Module, listen string) error {
func namespace(modules []rsyncd.Module, listen string) error {
if os.Getenv("GOKRAZY_RSYNC_NAMESPACE") != "" {
log.Printf("pid %d (inside Linux mount/pid namespace)", os.Getpid())

Expand Down
13 changes: 8 additions & 5 deletions internal/rsynctest/rsynctest.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import (
"github.com/gokrazy/rsync/internal/anonssh"
"github.com/gokrazy/rsync/internal/config"
"github.com/gokrazy/rsync/internal/maincmd"
"github.com/gokrazy/rsync/internal/rsyncd"
"github.com/gokrazy/rsync/rsyncd"
"github.com/google/go-cmp/cmp"
"golang.org/x/sys/unix"
)
Expand All @@ -37,8 +37,8 @@ type TestServer struct {

// InteropModule is a convenience function to define an rsync module named
// “interop” with the specified path.
func InteropModule(path string) []config.Module {
return []config.Module{
func InteropModule(path string) []rsyncd.Module {
return []rsyncd.Module{
{
Name: "interop",
Path: path,
Expand All @@ -60,7 +60,7 @@ func Listener(ln net.Listener) Option {
}
}

func New(t *testing.T, modules []config.Module, opts ...Option) *TestServer {
func New(t *testing.T, modules []rsyncd.Module, opts ...Option) *TestServer {
ts := &TestServer{}
for _, opt := range opts {
opt(ts)
Expand All @@ -70,7 +70,10 @@ func New(t *testing.T, modules []config.Module, opts ...Option) *TestServer {
{Rsyncd: "localhost:0"},
}
}
srv := rsyncd.NewServer(modules...)
srv, err := rsyncd.NewServer(modules)
if err != nil {
t.Fatal(err)
}

if ts.listener == nil {
ln, err := net.Listen("tcp", "localhost:0")
Expand Down
3 changes: 2 additions & 1 deletion interop_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/gokrazy/rsync/internal/config"
"github.com/gokrazy/rsync/internal/maincmd"
"github.com/gokrazy/rsync/internal/rsynctest"
"github.com/gokrazy/rsync/rsyncd"
"github.com/google/go-cmp/cmp"
)

Expand Down Expand Up @@ -357,7 +358,7 @@ func TestInteropRemoteDaemon(t *testing.T) {
// in remote daemon mode, rsync needs a config file, so we create one and
// set the HOME environment variable such that gokr-rsyncd will pick it up.
cfg := config.Config{
Modules: []config.Module{
Modules: []rsyncd.Module{
{Name: "interop", Path: source},
},
}
Expand Down
File renamed without changes.
3 changes: 1 addition & 2 deletions internal/rsyncd/flist.go → rsyncd/flist.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"sync"

"github.com/gokrazy/rsync"
"github.com/gokrazy/rsync/internal/config"
"github.com/gokrazy/rsync/internal/rsyncwire"
)

Expand All @@ -20,7 +19,7 @@ var (
)

// rsync/flist.c:send_file_list
func (st *sendTransfer) sendFileList(mod config.Module, opts *Opts, paths []string) (*fileList, error) {
func (st *sendTransfer) sendFileList(mod Module, opts *Opts, paths []string) (*fileList, error) {
var fileList fileList
fec := &rsyncwire.Buffer{}

Expand Down
File renamed without changes.
File renamed without changes.
39 changes: 31 additions & 8 deletions internal/rsyncd/rsyncd.go → rsyncd/rsyncd.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package rsyncd
import (
"bufio"
"context"
"errors"
"fmt"
"io"
"log"
Expand All @@ -11,7 +12,6 @@ import (
"strings"

"github.com/gokrazy/rsync"
"github.com/gokrazy/rsync/internal/config"
"github.com/gokrazy/rsync/internal/rsyncwire"
)

Expand All @@ -25,22 +25,34 @@ type sendTransfer struct {
lastMatch int64
}

func NewServer(modules ...config.Module) *Server {
return &Server{modules}
type Module struct {
Name string `toml:"name"`
Path string `toml:"path"`
ACL []string `toml:"acl"`
}

func NewServer(modules []Module) (*Server, error) {
for _, mod := range modules {
if err := validateModule(mod); err != nil {
return nil, err
}
}

return &Server{modules}, nil
}

type Server struct {
modules []config.Module
modules []Module
}

func (s *Server) getModule(requestedModule string) (config.Module, error) {
func (s *Server) getModule(requestedModule string) (Module, error) {
for _, mod := range s.modules {
if mod.Name == requestedModule {
return mod, nil
}
}

return config.Module{}, fmt.Errorf("no such module: %s", requestedModule)
return Module{}, fmt.Errorf("no such module: %s", requestedModule)
}

func (s *Server) formatModuleList() string {
Expand Down Expand Up @@ -190,7 +202,7 @@ func (s *Server) HandleDaemonConn(ctx context.Context, conn io.ReadWriter, remot
log.Printf("client %v requested rsync module %q", remoteAddr, requestedModule)
module, err := s.getModule(requestedModule)
if err != nil {
fmt.Fprintf(cwr, "@ERROR: Unknown module '%s'\n", requestedModule)
fmt.Fprintf(cwr, "@ERROR: Unknown module %q\n", requestedModule)
stapelberg marked this conversation as resolved.
Show resolved Hide resolved
return err
}

Expand Down Expand Up @@ -263,7 +275,7 @@ func (s *Server) HandleDaemonConn(ctx context.Context, conn io.ReadWriter, remot
}

// handleConn is equivalent to rsync/main.c:start_server
func (s *Server) HandleConn(module config.Module, rd io.Reader, crd *countingReader, cwr *countingWriter, paths []string, opts *Opts, negotiate bool) (err error) {
func (s *Server) HandleConn(module Module, rd io.Reader, crd *countingReader, cwr *countingWriter, paths []string, opts *Opts, negotiate bool) (err error) {
// “SHOULD be unique to each connection” as per
// https://github.com/JohannesBuchner/Jarsync/blob/master/jarsync/rsync.txt
//
Expand Down Expand Up @@ -396,3 +408,14 @@ func (s *Server) Serve(ctx context.Context, ln net.Listener) error {
}()
}
}

func validateModule(mod Module) error {
if mod.Name == "" {
return errors.New("module has no name")
}
if mod.Path == "" {
return fmt.Errorf("module %q has empty path", mod.Name)
}

stapelberg marked this conversation as resolved.
Show resolved Hide resolved
return nil
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.