Remove Windows support

Unfortunately, I don't have the expertise or bandwidth to maintain
the Windows support in Litestream. I'm open to re-adding support in
the future but right now it is hindering development and is not
well-tested or well-used.
This commit is contained in:
Ben Johnson
2022-02-05 08:11:21 -07:00
parent 4349398ff5
commit 8009bcf654
7 changed files with 13 additions and 183 deletions

View File

@@ -17,6 +17,7 @@ import (
"regexp"
"strconv"
"strings"
"syscall"
"time"
"github.com/benbjohnson/litestream"
@@ -67,13 +68,6 @@ func NewMain(stdin io.Reader, stdout, stderr io.Writer) *Main {
// Run executes the program.
func (m *Main) Run(ctx context.Context, args []string) (err error) {
// Execute replication command if running as a Windows service.
if isService, err := isWindowsService(); err != nil {
return err
} else if isService {
return runWindowsService(ctx)
}
// Copy "LITESTEAM" environment credentials.
applyLitestreamEnv()
@@ -98,7 +92,7 @@ func (m *Main) Run(ctx context.Context, args []string) (err error) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
signalCh := make(chan os.Signal, 1)
signal.Notify(signalCh, notifySignals...)
signal.Notify(signalCh, syscall.SIGINT, syscall.SIGTERM)
if err := c.Run(ctx); err != nil {
return err
@@ -685,7 +679,7 @@ func DefaultConfigPath() string {
if v := os.Getenv("LITESTREAM_CONFIG"); v != "" {
return v
}
return defaultConfigPath
return "/etc/litestream.yml"
}
func registerConfigFlag(fs *flag.FlagSet, configPath *string, noExpandEnv *bool) {

View File

@@ -1,21 +0,0 @@
// +build !windows
package main
import (
"context"
"os"
"syscall"
)
const defaultConfigPath = "/etc/litestream.yml"
func isWindowsService() (bool, error) {
return false, nil
}
func runWindowsService(ctx context.Context) error {
panic("cannot run windows service as unix process")
}
var notifySignals = []os.Signal{syscall.SIGINT, syscall.SIGTERM}

View File

@@ -1,108 +0,0 @@
//go:build windows
// +build windows
package main
import (
"context"
"io"
"log"
"os"
"golang.org/x/sys/windows"
"golang.org/x/sys/windows/svc"
"golang.org/x/sys/windows/svc/eventlog"
)
const defaultConfigPath = `C:\Litestream\litestream.yml`
// serviceName is the Windows Service name.
const serviceName = "Litestream"
// isWindowsService returns true if currently executing within a Windows service.
func isWindowsService() (bool, error) {
return svc.IsWindowsService()
}
func runWindowsService(ctx context.Context) error {
// Attempt to install new log service. This will fail if already installed.
// We don't log the error because we don't have anywhere to log until we open the log.
_ = eventlog.InstallAsEventCreate(serviceName, eventlog.Error|eventlog.Warning|eventlog.Info)
elog, err := eventlog.Open(serviceName)
if err != nil {
return err
}
defer elog.Close()
// Set eventlog as log writer while running.
log.SetOutput((*eventlogWriter)(elog))
defer log.SetOutput(os.Stdout)
log.Print("Litestream service starting")
if err := svc.Run(serviceName, &windowsService{ctx: ctx}); err != nil {
return errExit
}
log.Print("Litestream service stopped")
return nil
}
// windowsService is an interface adapter for svc.Handler.
type windowsService struct {
ctx context.Context
}
func (s *windowsService) Execute(args []string, r <-chan svc.ChangeRequest, statusCh chan<- svc.Status) (svcSpecificEC bool, exitCode uint32) {
var err error
// Notify Windows that the service is starting up.
statusCh <- svc.Status{State: svc.StartPending}
// Instantiate replication command and load configuration.
c := NewReplicateCommand()
if c.Config, err = ReadConfigFile(DefaultConfigPath(), true); err != nil {
log.Printf("cannot load configuration: %s", err)
return true, 1
}
// Execute replication command.
if err := c.Run(s.ctx); err != nil {
log.Printf("cannot replicate: %s", err)
statusCh <- svc.Status{State: svc.StopPending}
return true, 2
}
// Notify Windows that the service is now running.
statusCh <- svc.Status{State: svc.Running, Accepts: svc.AcceptStop}
for {
select {
case req := <-r:
switch req.Cmd {
case svc.Stop:
c.Close()
statusCh <- svc.Status{State: svc.StopPending}
return false, windows.NO_ERROR
case svc.Interrogate:
statusCh <- req.CurrentStatus
default:
log.Printf("Litestream service received unexpected change request cmd: %d", req.Cmd)
}
}
}
}
// Ensure implementation implements io.Writer interface.
var _ io.Writer = (*eventlogWriter)(nil)
// eventlogWriter is an adapter for using eventlog.Log as an io.Writer.
type eventlogWriter eventlog.Log
func (w *eventlogWriter) Write(p []byte) (n int, err error) {
elog := (*eventlog.Log)(w)
return 0, elog.Info(1, string(p))
}
var notifySignals = []os.Signal{os.Interrupt}

1
go.mod
View File

@@ -13,7 +13,6 @@ require (
github.com/prometheus/client_golang v1.12.1
golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9
google.golang.org/api v0.66.0
gopkg.in/yaml.v2 v2.4.0
)

View File

@@ -132,7 +132,7 @@ func MkdirAll(path string, mode os.FileMode, uid, gid int) error {
if j > 1 {
// Create parent.
err = MkdirAll(fixRootDirectory(path[:j-1]), mode, uid, gid)
err = MkdirAll(path[:j-1], mode, uid, gid)
if err != nil {
return err
}
@@ -154,6 +154,15 @@ func MkdirAll(path string, mode os.FileMode, uid, gid int) error {
return nil
}
// Fileinfo returns syscall fields from a FileInfo object.
func Fileinfo(fi os.FileInfo) (uid, gid int) {
if fi == nil {
return -1, -1
}
stat := fi.Sys().(*syscall.Stat_t)
return int(stat.Uid), int(stat.Gid)
}
// ParseSnapshotPath parses the index from a snapshot filename. Used by path-based replicas.
func ParseSnapshotPath(s string) (index int, err error) {
a := snapshotPathRegex.FindStringSubmatch(s)

View File

@@ -1,21 +0,0 @@
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris
package internal
import (
"os"
"syscall"
)
// Fileinfo returns syscall fields from a FileInfo object.
func Fileinfo(fi os.FileInfo) (uid, gid int) {
if fi == nil {
return -1, -1
}
stat := fi.Sys().(*syscall.Stat_t)
return int(stat.Uid), int(stat.Gid)
}
func fixRootDirectory(p string) string {
return p
}

View File

@@ -1,22 +0,0 @@
// +build windows
package internal
import (
"os"
)
// Fileinfo returns syscall fields from a FileInfo object.
func Fileinfo(fi os.FileInfo) (uid, gid int) {
return -1, -1
}
// fixRootDirectory is copied from the standard library for use with mkdirAll()
func fixRootDirectory(p string) string {
if len(p) == len(`\\?\c:`) {
if os.IsPathSeparator(p[0]) && os.IsPathSeparator(p[1]) && p[2] == '?' && os.IsPathSeparator(p[3]) && p[5] == ':' {
return p + `\`
}
}
return p
}