Harden backup and restore operations
This commit is contained in:
+38
-4
@@ -23,6 +23,18 @@ type Runner struct {
|
||||
processes map[string]*os.Process
|
||||
}
|
||||
|
||||
func (r *Runner) SetBinary(path string) {
|
||||
r.mu.Lock()
|
||||
r.Binary = path
|
||||
r.mu.Unlock()
|
||||
}
|
||||
|
||||
func (r *Runner) binary() string {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
return r.Binary
|
||||
}
|
||||
|
||||
type Progress struct {
|
||||
Percent float64
|
||||
Bytes int64
|
||||
@@ -43,7 +55,7 @@ var (
|
||||
|
||||
func (r *Runner) Run(ctx context.Context, runID string, job model.Job, callback func(Progress)) (Summary, error) {
|
||||
args := Arguments(job)
|
||||
cmd := exec.CommandContext(ctx, r.Binary, args...)
|
||||
cmd := exec.CommandContext(ctx, r.binary(), args...)
|
||||
cmd.Env = append(os.Environ(), "LC_ALL=C", "LANG=C")
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
|
||||
cmd.Cancel = func() error {
|
||||
@@ -92,9 +104,11 @@ func (r *Runner) Run(ctx context.Context, runID string, job model.Job, callback
|
||||
})
|
||||
close(done)
|
||||
}()
|
||||
errBytes, _ := io.ReadAll(io.LimitReader(stderr, 1024*1024))
|
||||
stderrDone := make(chan []byte, 1)
|
||||
go func() { stderrDone <- drainLimited(stderr, 1024*1024) }()
|
||||
err = cmd.Wait()
|
||||
<-done
|
||||
errBytes := <-stderrDone
|
||||
if err != nil {
|
||||
if errors.Is(ctx.Err(), context.Canceled) {
|
||||
return Summary{}, fmt.Errorf("cancelled: %w", ctx.Err())
|
||||
@@ -117,7 +131,7 @@ func (r *Runner) Estimate(ctx context.Context, runID string, job model.Job) (Sum
|
||||
args[index] = "--out-format="
|
||||
}
|
||||
}
|
||||
cmd := exec.CommandContext(ctx, r.Binary, args...)
|
||||
cmd := exec.CommandContext(ctx, r.binary(), args...)
|
||||
cmd.Env = append(os.Environ(), "LC_ALL=C", "LANG=C")
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
|
||||
cmd.Cancel = func() error {
|
||||
@@ -158,9 +172,11 @@ func (r *Runner) Estimate(ctx context.Context, runID string, job model.Job) (Sum
|
||||
})
|
||||
close(done)
|
||||
}()
|
||||
errBytes, _ := io.ReadAll(io.LimitReader(stderr, 1024*1024))
|
||||
stderrDone := make(chan []byte, 1)
|
||||
go func() { stderrDone <- drainLimited(stderr, 1024*1024) }()
|
||||
err = cmd.Wait()
|
||||
<-done
|
||||
errBytes := <-stderrDone
|
||||
if err != nil {
|
||||
if errors.Is(ctx.Err(), context.Canceled) {
|
||||
return Summary{}, fmt.Errorf("cancelled: %w", ctx.Err())
|
||||
@@ -258,6 +274,24 @@ func scanOutput(reader io.Reader, callback func(string)) {
|
||||
}
|
||||
}
|
||||
|
||||
func drainLimited(reader io.Reader, limit int) []byte {
|
||||
result := make([]byte, 0, limit)
|
||||
buffer := make([]byte, 32*1024)
|
||||
for {
|
||||
count, err := reader.Read(buffer)
|
||||
if count > 0 && len(result) < limit {
|
||||
keep := count
|
||||
if keep > limit-len(result) {
|
||||
keep = limit - len(result)
|
||||
}
|
||||
result = append(result, buffer[:keep]...)
|
||||
}
|
||||
if err != nil {
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func splitLinesAndCarriageReturns(data []byte, atEOF bool) (advance int, token []byte, err error) {
|
||||
for i, value := range data {
|
||||
if value == '\n' || value == '\r' {
|
||||
|
||||
Reference in New Issue
Block a user