diff --git a/changelog/unreleased/issue-5683 b/changelog/unreleased/issue-5683 new file mode 100644 index 000000000..b6a3f422c --- /dev/null +++ b/changelog/unreleased/issue-5683 @@ -0,0 +1,9 @@ +Bugfix: Prevent `backup --stdin-from-command` from hanging + +When using `--stdin-from-command`, the `backup` command could hang until +manually cancelled if the backup stopped before all subprocess output was +consumed, for example after a failed upload to the repository. This has been +fixed. + +https://github.com/restic/restic/issues/5683 +https://github.com/restic/restic/pull/21829 diff --git a/internal/fs/fs_reader_command.go b/internal/fs/fs_reader_command.go index a1371fd1e..dabf54659 100644 --- a/internal/fs/fs_reader_command.go +++ b/internal/fs/fs_reader_command.go @@ -97,5 +97,8 @@ func (fp *CommandReader) Close() error { return nil } + // This line can only be reached if the command was not read until the end. This can, for example, + // happen if a backup run is interrupted. Kill the command to avoid hanging. + _ = fp.cmd.Cancel() return fp.wait() } diff --git a/internal/fs/fs_reader_command_test.go b/internal/fs/fs_reader_command_test.go index 5f27e9590..214b1003e 100644 --- a/internal/fs/fs_reader_command_test.go +++ b/internal/fs/fs_reader_command_test.go @@ -6,6 +6,7 @@ import ( "io" "strings" "testing" + "time" "github.com/restic/restic/internal/fs" "github.com/restic/restic/internal/test" @@ -51,3 +52,14 @@ func TestCommandReaderOutput(t *testing.T) { test.Equals(t, "hello world", strings.TrimSpace(buf.String())) } + +func TestCommandReaderQuickClose(t *testing.T) { + ctx, cancel := context.WithTimeout(context.TODO(), 10*time.Second) + defer cancel() + reader, err := fs.NewCommandReader(ctx, []string{"sleep", "3600"}, func(msg string, args ...interface{}) {}) + test.OK(t, err) + + // test that close returns before the context expires + _ = reader.Close() + test.OK(t, ctx.Err()) +}