Status line truncated / cut off?

When doing a restic backup, the status line (which shows the % complete, size, # of files, etc.) sometimes doesn’t show the whole line - the part of the ETA gets cut off.

My command window is plenty wide.

Is there an inherent limit to the length of the status line?

There isn’t. Which OS and terminal emulator do you use? restic tries to find out how wide the window currently is and cuts off the status line accordingly. Can you reproduce the issue?

I’ve seen some weird display bugs (with restic and rclone) when the command prompt window was resized. Can you try changing the default size of the command prompt and not resizing it after its creation?

I have more information. I am using this version of restic on my Windows 10 machine:

restic 0.9.3 (v0.9.3-62-gc8fc7236) compiled with go1.10.2 on windows/amd64

I do most of my work in the Cygwin command window, which is a Unix-like wrapper.

If I run the command in the stock Windows Command Prompt window, it works fine. If I run it in the Cygwin window (mintty) it is truncated.

The window is properly sized before I run the command - I am not resizing it while the restic command is running.

So it looks like Cygwin may be messing with restic's ability to determine the correct window size.

More information:
When I am running in the Cygwin window, I also never see the following messages:

repository b94e8466 opened successfully, password is correct
found 3 old cache directories in C:\Users\XXXX\AppData\Local\restic, pass --cleanup-cache to remove them

Looking at the restic source, both of those messages are conditioned by restic checking if the output is a TTY.

Apparently Cygwin is interfering with that check as well. Makes sense, since both the stdoutIsTerminal and stdoutTerminalWidth functions refer to the “golang.org/x/crypto/ssh/terminal” import.

Hello,
Sorry to revive this old post, but I think I have the same issue and I don’t seem to find a way around it or at least find the root cause.

I believe I’m getting the default 80 columns as specified here (since the output, like the ETA is getting truncated to 80 lines):

To check if there is something wrong with my config I did some experimenting:

+ echo 'COLUMNS: $COLUMNS'
COLUMNS: 123

+ printf '%*s\n' \"${COLUMNS:-$(tput cols)}\" '' | tr ' ' -
---------------------------------------------------------------------------------------------------------------------------

+ python -c "import os; print(os.popen('stty size', 'r').read())"
24 123

And as you can see, these traditional methods of retrieving the size work (for my 123 wide terminal).

But still restic does not like my terminal:

ID        Time                 Host              Tags        Paths
------------------------------------------------------------------------------------
00000000  2021-02-02 00:00:00  my-host                       /some-data             
------------------------------------------------------------------------------------

Unfortunately haven’t been able to test what is the output of func GetSize(fd int) (width, height int, err error) in an isolated go program. (I need to learn some go basics :sweat_smile: )

But you may ask what’s your terminal? :laughing:
I run restic with docker + k8s with tty: true
This might add just a spice of complexity, that’s why I made an extra effort debugging, but I haven’t been able to pinpint the issue.

Is it on go’s GetSize? Restic? My setup?

Okay, after a few more attempts I found out that the problem might be from the lib (terminal · pkg.go.dev)

Since I’m getting both termWidth and termHeight as 0 :exploding_head:

Hm, interesting. Restic uses the ioctl TIOCGWINSZ on the file descriptor (here)

  // GetSize returns the dimensions of the given terminal.
  func GetSize(fd int) (width, height int, err error) {
      ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ)
      if err != nil {
          return -1, -1, err
      }
      return int(ws.Col), int(ws.Row), nil
  }

It looks like this call returned either 0, 0 or an error, in which case -1 is returned and then converted to 0 by restic here:

	// only truncate interactive status output
	var width int
	if t.canUpdateStatus {
		var err error
		width, _, err = terminal.GetSize(int(t.fd))
		if err != nil || width <= 0 {
			// use 80 columns by default
			width = 80
		}
	}

I don’t know why the ioctl failed, you could try to run this gist (via git clone https://gist.github.com/7a11999c5d0670287517527099e20879.git):

git clone https://gist.github.com/7a11999c5d0670287517527099e20879.git
cd 7a11999c5d0670287517527099e20879
go build
./termsizewidthtest

Make sure to run it in the same way as restic. I’ve also included a binary for Linux/amd64.

Thank you for your reply and the sample code.
I have been experimenting, and it always returns 0x0 and no error.

After talking with some go people I found that we should be using the new golang.org/x/term instead of golang.org/x/crypto/ssh/terminal

Thus, I’ve edited your script to test with both packages:

main.go
package main

import (
	"fmt"
	"os"

	"golang.org/x/crypto/ssh/terminal"
	"golang.org/x/term"
)

func main() {
	termID := int(os.Stdout.Fd())

	width, height, err := terminal.GetSize(termID)

	fmt.Printf("crypto/ssh/terminal: width %v, height %v, err %v\n", width, height, err)

	width, height, err = term.GetSize(termID)

	fmt.Printf("term: width %v, height %v, err %v\n", width, height, err)
}

And the output is :unamused::

+ ./termsizewidthtest
crypto/ssh/terminal: width 0, height 0, err <nil>
term: width 0, height 0, err <nil>
For referenece here is the output of `stty -a`
+ stty -a
speed 38400 baud; rows 24; columns 200; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>; eol2 = <undef>; swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; flush = ^O;
min = 1; time = 0;
-parenb -parodd -cmspar cs8 -hupcl -cstopb cread -clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff -iuclc -ixany -imaxbel -iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke -flusho -extproc

So we can see that the problem is on the go side and not on restic (or in my setup).

But:

  • Restic should update to the new golang.org/x/term
  • If the width of the terminal is 0, I it is not reasonable to trim the output - this is a bug
  • When piped or TERM="dumb" restic hides the progress logs even with --verbose is it intentional? Why?
  • Lots of things in internal/ui/termstatus/ seem, quote, “not well-written” and “complex”

So, @fd0, what is your vision on the terminal status and how it should work?
Why is it so hard to have a simple output where restic spits out line by line of what it is doing?

Thanks for the feedback!

I agree. We can switch this over so that when 0 is returned but no error is reported, restic will not truncate the line.

But there’s still something else going on. If stty is able to figure out what the terminal width is in your case, why not either terminal libraries?

Can you please run both commands again using strace -e ioctl and paste the output?

Progress information is only displayed on interactive terminals.

For what it’s worth: I agree. UI handling is surprisingly complex.