[pve-devel] [RFC PATCH storage] pvesm export: convert dd's \r in status=progress output to \n

Thomas Lamprecht t.lamprecht at proxmox.com
Wed Jan 15 12:28:33 CET 2025


Am 15.01.25 um 10:59 schrieb Dominik Csapak:
> pvesm export is mostly used for (remote) migrations, where the
> status progress output lands in a task log. For task logs we want to
> have line based output (since it's not a terminal), but dd uses \r
> to overwrite the same line which does not work in every situation, e.g.
> browsers sometimes simply don't show them, making the dd output a long
> line instead of separate ones.
> 
> To fix this, use run_command's `errfunc` to log the lines. run_command
> will split also on \r, but with warn we print a \n so this does the
> conversion.
> 
> This fixes an issue where the remote migration task log on PDM does not
> display that part in new lines. (ExtJS works because it does things
> differently and some browser quirks convert \r to \n)
> 
> Signed-off-by: Dominik Csapak <d.csapak at proxmox.com>
> ---
> Not sure if we want to take this approach because we lose the
> functionalty of overwriting progress on the terminal.

FWIW, you could test with `-t STDERR` if the std error FD is a terminal
and differ between replacing \r or not.

> 
> AFAICS there is no easy way to only do this for the task log, since
> we simply pipe the output fh of the worker task to the task log fh.
> 
> Alternatively we could patch the task log api to parse \r as newlines or
> patch the yew widget toolkit to replace \r with \n.

Wouldn't be one alternative also be to do that in the UI?

> 
> No real preference from my side, but this patch fixes the task log file
> without touching our central worker task code, so it seemed sensible.
> 
>  src/PVE/Storage/ISCSIPlugin.pm |  6 +++++-
>  src/PVE/Storage/LVMPlugin.pm   |  6 +++++-
>  src/PVE/Storage/Plugin.pm      | 10 ++++++++--
>  3 files changed, 18 insertions(+), 4 deletions(-)
> 
> diff --git a/src/PVE/Storage/ISCSIPlugin.pm b/src/PVE/Storage/ISCSIPlugin.pm
> index eb70453..33604cd 100644
> --- a/src/PVE/Storage/ISCSIPlugin.pm
> +++ b/src/PVE/Storage/ISCSIPlugin.pm
> @@ -631,7 +631,11 @@ sub volume_export {
>  	$size = int($1);
>      });
>      PVE::Storage::Plugin::write_common_header($fh, $size);
> -    run_command(['dd', "if=$file", "bs=64k", "status=progress"], output => '>&'.fileno($fh));
> +    run_command(['dd', "if=$file", "bs=64k", "status=progress"], output => '>&'.fileno($fh), errfunc => sub {
> +	# convert dd's \r to \n

I'd move the comment into the line re-printing the output, shorter
and also avoids having something before extracting the parameters,
which should always be first in perl for now; once we widely switch
to using signatures that wouldn't be a problem anymore.

Maybe also add some reasoning to the comment, like: "convert \r to \n to avoid issues in browsers"

> +	my ($line) = @_;
> +	warn "$line\n";
> +    });

Why not use `print STDERR "$line\n";`?

Because warn could be caught by a $SIG{__WARN__} handler from the call chain and
interpreted as problem. For relaying stderr messages to stderr again printing
directly to STDERR feels a bit more expressive and safer to me.

>      return;
>  }
>  
> diff --git a/src/PVE/Storage/LVMPlugin.pm b/src/PVE/Storage/LVMPlugin.pm
> index 38f7fa1..d41647b 100644
> --- a/src/PVE/Storage/LVMPlugin.pm
> +++ b/src/PVE/Storage/LVMPlugin.pm
> @@ -647,7 +647,11 @@ sub volume_export {
>  	$size = int($1);
>      });
>      PVE::Storage::Plugin::write_common_header($fh, $size);
> -    run_command(['dd', "if=$file", "bs=64k", "status=progress"], output => '>&'.fileno($fh));
> +    run_command(['dd', "if=$file", "bs=64k", "status=progress"], output => '>&'.fileno($fh), errfunc => sub {
> +	# convert dd's \r to \n
> +	my ($line) = @_;
> +	warn "$line\n";

same as above

> +    });
>  }
>  
>  sub volume_import_formats {
> diff --git a/src/PVE/Storage/Plugin.pm b/src/PVE/Storage/Plugin.pm
> index 65cf43f..c42a675 100644
> --- a/src/PVE/Storage/Plugin.pm
> +++ b/src/PVE/Storage/Plugin.pm
> @@ -1703,11 +1703,17 @@ sub volume_export {
>  	my $file_format = ($class->parse_volname($volname))[6];
>  	my $size = file_size_info($file, undef, $file_format);
>  
> +	# convert dd's \r to \n
> +	my $errfunc = sub {
> +	    my ($line) = @_;
> +	    warn "$line\n";

same here

> +	};
> +
>  	if ($format eq 'raw+size') {
>  	    die $err_msg if $with_snapshots || $file_format eq 'subvol';
>  	    write_common_header($fh, $size);
>  	    if ($file_format eq 'raw') {
> -		run_command(['dd', "if=$file", "bs=4k", "status=progress"], output => '>&'.fileno($fh));
> +		run_command(['dd', "if=$file", "bs=64k", "status=progress"], output => '>&'.fileno($fh), errfunc => $errfunc);
>  	    } else {
>  		run_command(['qemu-img', 'convert', '-f', $file_format, '-O', 'raw', $file, '/dev/stdout'],
>  		            output => '>&'.fileno($fh));
> @@ -1717,7 +1723,7 @@ sub volume_export {
>  	    my $data_format = $1;
>  	    die $err_msg if !$with_snapshots || $file_format ne $data_format;
>  	    write_common_header($fh, $size);
> -	    run_command(['dd', "if=$file", "bs=4k", "status=progress"], output => '>&'.fileno($fh));
> +	    run_command(['dd', "if=$file", "bs=64k", "status=progress"], output => '>&'.fileno($fh), errfunc => $errfunc);
>  	    return;
>  	} elsif ($format eq 'tar+size') {
>  	    die $err_msg if $file_format ne 'subvol';





More information about the pve-devel mailing list