Приведите scp(1), sftp(1) или rsync(1) к передаче именованного канала
Я намереваюсь передать полные и инкрементные резервные копии моих подобъемов btrfs в службу архивации на магнитную ленту Служба предоставляет конечные точки FTP и SSH. Если бы мне было разрешено выполнять произвольные команды на конечной точке SSH, я бы сделал следующее для инкрементного резервного копирования:
btrfs send -p $LAST_SUBVOLUME $NEXT_SUBVOLUME | compress | encrypt |
ssh -p $PORT $USER@$ENDPOINT "cat > $SUBVOLUME.$YYYYMMDD.btrfs.bz2.gpg"
Мне, однако, не разрешено делать это:
$ ssh -p $PORT $USER@$ENDPOINT
Last login: Mon Jan 3 01:23:45 2067 from 123.456.789.123
This account is restricted by rssh.
Allowed commands: scp sftp rsync
If you believe this is in error, please contact your system administrator.
Connection to some.remote.endpoint closed.
Поэтому вместо этого я решил использовать протокол SCP для передачи. Тем не менее, мой scp
бинарный отказывается передавать именованный канал:
$ scp -P $PORT <(btrfs send -p $LAST_SUBVOLUME $NEXT_SUBVOLUME | compress | encrypt) \
$USER@$ENDPOINT:$SUBVOLUME.$YYYYMMDD.btrfs.bz2.gpg
/dev/fd/63: not a regular file
Ирония в том, что, видимо, scp
когда-то делал правильные вещи. Я предполагаю, что не все думали, что передача именованных каналов может быть вменяемой / полезной. РЕДАКТИРОВАТЬ (2017-06-03): Это было неправильное наблюдение. Как отмечает Кенстер, протокол SCP не позволяет отправлять файлы неизвестного размера.
ОБНОВЛЕНИЕ (2017-06-04): Я также попытался передать данные, используя протокол SFTP. По-видимому, протокол FTP позволяет отправлять файлы неизвестного размера, о чем свидетельствует поддержка передачи данных в ftp
( ссылка) и ncftpput
( ссылка, раздел Описание, последний абзац) бинарные файлы. Я не нашел такой поддержки в клиентах SFTP, которые я пробовал (sftp
, lftp
), что может указывать на то, что SFTP (в отличие от FTP) не поддерживает отправку файлов неизвестного размера (в отличие от того, что я думал, SFTP - это не просто FTP, туннелированный через SSH; это другой протокол).
ОБНОВЛЕНИЕ (2017-06-05): Согласно интернет-проекту протокола SFTP версии 3 ( ссылка), каждый пакет уровня приложения должен указывать длину полезной нагрузки перед полезной нагрузкой ( ссылка, раздел 3). Однако SFTP поддерживает поиск в записанном файле ( ссылка, раздел 6.4) с явной поддержкой записи после текущего конца файла. Поэтому, если это возможно, можно использовать небольшой буфер на стороне клиента и отправлять файл неизвестного размера небольшими порциями известного размера:
#!/bin/bash
# <Exchange SSH_FXP_INIT requests.>
# <Send an SSH_FXP_OPEN request.>
CHUNK_SIZE=32768
OFFSET=0
IFS=''; while read -r -N $CHUNK_SIZE CHUNK; do
ACTUAL_SIZE=`cat <<<"$CHUNK" | head -c -1 | wc -c`
# <Send an SSH_FXP_WRITE request with payload $CHUNK of size
# $ACTUAL_SIZE at offset $OFFSET.>
OFFSET=$(($OFFSET+$ACTUAL_SIZE))
done < <(command)
# <Send an SSH_FXP_CLOSE request.>
Однако выполнение связи вручную через оболочку было бы довольно болезненным. Я ищу клиента SFTP, который предоставляет такую функциональность.
Рекомендации
3 ответа
lftp
4.6.1 и новее должны быть в состоянии сделать это: https://github.com/lavv17/lftp/issues/104
К сожалению, команда, предложенная в связанной проблеме, не работает, но слегка измененная:
lftp -p $port sftp://$user:$pass@$host -e "put /dev/stdin -o $filename"
Из-за ошибки вы должны указать что-то в поле пароля, если вы используете SSH-агент. Но любая случайная строка подойдет.
Эта команда читает из /dev/stdin
, Если вы должны использовать другой именованный канал, я думаю, что это должно работать тоже - если нет, вы всегда можете cat named-pipe | lftp ...
,
Я проверял lftp
с загрузкой>100GB без проблем.
rclone
также поддерживает SFTP и должен иметь возможность загружать данные по каналу с rcat
команда, которая должна быть выпущена в 1.38 (следующая версия).
SCP не подходит для ваших целей. Протокол SCP не поддерживает отправку потока байтов неизвестного размера в удаленную систему для сохранения в виде файла. Сообщение протокола SCP для отправки файла требует, чтобы размер файла был отправлен первым, а затем байты, составляющие файл. С потоком байтов, считанных из канала, вы, как правило, не знаете, сколько байтов собирается создать канал, поэтому нет способа отправить сообщение протокола SCP, включая правильный размер.
(Единственное онлайн-описание протокола SCP, которое я смог найти, здесь и здесь. Сосредоточьтесь на описании сообщения "С".)
Протокол SFTP может быть использован для такого рода вещей. Насколько я знаю, нормальный sftp
Утилита командной строки не поддерживает чтение канала и сохранение его в виде удаленного файла. Но есть библиотеки SSH/SFTP для большинства современных языков программирования (perl, python, ruby, C#, Java, C и т. Д.). Если вы знаете, как использовать один из этих языков, написать программу, которая будет делать то, что вам нужно, не составит труда.
Если вы застряли в сценариях оболочки, возможно, достаточно подделать протокол SCP для передачи файла. Вот пример:
#!/bin/bash
cmd='cat /etc/group'
size=$($cmd | wc -c)
{
echo C0644 $size some-file
$cmd
echo -n -e '\000'
} | ssh user@host scp -v -p -t /some/directory
Это создаст some-file
в /some/directory
в удаленной системе с разрешениями 644. Содержимое файла будет любым $cmd
пишет в свой стандартный вывод. Обратите внимание, что вы запускаете команду дважды, независимо от того, какое потребление ресурсов и побочные эффекты это подразумевает. И команда должна выводить одинаковое количество байтов каждый раз.
Если вы можете написать Perl, вы можете попробовать использовать Net:: SFTP:: Foreign, который может передавать файлы из дескриптора открытого файла через SFTP:
#!/usr/bin/perl
# untested!
use Net::SFTP::Foreign;
my $user = ...;
my $host = ...;
my $remote_path = ...;
my $btrfs_cmd = 'btrfs send -p $LAST_SUBVOLUME $NEXT_SUBVOLUME';
my $sftp = Net::SFTP::Foreign->new("$user\@$host", ...);
open my($pipe), "$btrfs_cmd | compress | encrypt |"
or die $!;
$sftp->put($fh, $remote_path);