You've already forked pgbackrest
mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2025-07-03 00:26:59 +02:00
SFTP support for repository storage.
This commit is contained in:
@ -35,6 +35,18 @@
|
||||
<p>Block incremental backup.</p>
|
||||
</release-item>
|
||||
|
||||
<release-item>
|
||||
<github-pull-request id="1959"/>
|
||||
|
||||
<release-item-contributor-list>
|
||||
<release-item-contributor id="reid.thompson"/>
|
||||
<release-item-reviewer id="stephen.frost"/>
|
||||
<release-item-reviewer id="david.steele"/>
|
||||
</release-item-contributor-list>
|
||||
|
||||
<p><proper>SFTP</proper> support for repository storage.</p>
|
||||
</release-item>
|
||||
|
||||
<release-item>
|
||||
<github-pull-request id="2051"/>
|
||||
|
||||
|
@ -169,8 +169,15 @@
|
||||
<variable key="s3-key">accessKey1</variable>
|
||||
<variable key="s3-key-secret">verySecretKey1</variable>
|
||||
|
||||
<!-- SFTP Settings -->
|
||||
<variable key="sftp-all">n</variable> <!-- Build all the documentation with SFTP? -->
|
||||
<variable key="sftp-repo">demo-repo</variable>
|
||||
<variable key="sftp-host-key-hash-type">sha1</variable>
|
||||
<variable key="sftp-private-key-file">{[pg-home-path]}/.ssh/id_rsa_sftp</variable>
|
||||
<variable key="sftp-public-key-file">{[pg-home-path]}/.ssh/id_rsa_sftp.pub</variable>
|
||||
|
||||
<!-- Is any object store being used to build all the documentation? -->
|
||||
<variable key="object-any-all">('{[azure-all]}' eq 'y' || '{[gcs-all]}' eq 'y' || '{[s3-all]}' eq 'y')</variable>
|
||||
<variable key="object-any-all">('{[azure-all]}' eq 'y' || '{[gcs-all]}' eq 'y' || '{[s3-all]}' eq 'y' || '{[sftp-all]}' eq 'y')</variable>
|
||||
|
||||
<!-- Hosts -->
|
||||
<variable key="host-image">pgbackrest/doc:{[os-type]}</variable>
|
||||
@ -192,6 +199,12 @@
|
||||
<variable key="host-s3-id">s3</variable>
|
||||
<variable key="host-s3">s3-server</variable>
|
||||
|
||||
<variable key="host-sftp-id">sftp</variable>
|
||||
<variable key="host-sftp">sftp-server</variable>
|
||||
<variable key="host-sftp-user">{[host-user]}</variable>
|
||||
<variable key="host-sftp-image">{[host-image]}</variable>
|
||||
<variable key="host-sftp-mount">{[host-mount]}</variable>
|
||||
|
||||
<variable key="host-pg1-id">pg1</variable>
|
||||
<variable key="host-pg1">pg-primary</variable>
|
||||
<variable key="host-pg1-user">{[host-user]}</variable>
|
||||
@ -300,7 +313,7 @@
|
||||
rm /etc/apt/apt.conf.d/70debconf && \
|
||||
apt-get update && \
|
||||
apt-get install -y --no-install-recommends sudo ssh wget vim gnupg lsb-release iputils-ping ca-certificates \
|
||||
tzdata locales 2>&1
|
||||
tzdata locales libssh2-1-dev 2>&1
|
||||
|
||||
{[sudo-disable-core-dump]}
|
||||
|
||||
@ -361,6 +374,10 @@
|
||||
# Enable PowerTools repository (only available on RHEL8)
|
||||
RUN dnf config-manager --set-enabled powertools || true
|
||||
|
||||
# Install and enable epel repository
|
||||
RUN dnf -y install epel-release
|
||||
RUN crb enable
|
||||
|
||||
# Install CA certificate
|
||||
RUN update-ca-trust extract
|
||||
|
||||
@ -551,13 +568,13 @@
|
||||
|
||||
<execute if="{[os-type-is-debian]}" user="root" pre="y">
|
||||
<exe-cmd>
|
||||
apt-get install postgresql-client libxml2</exe-cmd>
|
||||
apt-get install postgresql-client libxml2 libssh2-1</exe-cmd>
|
||||
<exe-cmd-extra>-y 2>&1</exe-cmd-extra>
|
||||
</execute>
|
||||
|
||||
<execute if="{[os-type-is-rhel]}" user="root" pre="y">
|
||||
<exe-cmd>
|
||||
yum install postgresql-libs
|
||||
yum install postgresql-libs libssh2
|
||||
</exe-cmd>
|
||||
<exe-cmd-extra>-y 2>&1</exe-cmd-extra>
|
||||
</execute>
|
||||
@ -773,6 +790,57 @@
|
||||
<admonition type="note">The region and endpoint will need to be configured to where the bucket is located. The values given here are for the <id>{[s3-region]}</id> region.</admonition>
|
||||
</block-define>
|
||||
|
||||
<!-- ======================================================================================================================= -->
|
||||
<block-define id="sftp-setup">
|
||||
<p><backrest/> supports locating repositories on <proper>SFTP</proper> hosts. SFTP file transfer is relatively slow so commands benefit by increasing <br-option>process-max</br-option> to parallelize file transfer.</p>
|
||||
|
||||
<backrest-config host="{[sftp-setup-host]}" file="{[backrest-config-demo]}" owner="{[sftp-setup-config-owner]}">
|
||||
<title>Configure <proper>SFTP</proper></title>
|
||||
|
||||
<backrest-config-option section="global" key="repo{[sftp-setup-repo-id]}-type">sftp</backrest-config-option>
|
||||
<backrest-config-option section="global" key="repo{[sftp-setup-repo-id]}-path">/{[sftp-repo]}</backrest-config-option>
|
||||
<backrest-config-option section="global" key="repo{[sftp-setup-repo-id]}-bundle">y</backrest-config-option>
|
||||
<backrest-config-option section="global" key="repo{[sftp-setup-repo-id]}-sftp-host">{[host-sftp]}</backrest-config-option>
|
||||
<backrest-config-option section="global" key="repo{[sftp-setup-repo-id]}-sftp-host-key-hash-type">{[sftp-host-key-hash-type]}</backrest-config-option>
|
||||
<backrest-config-option section="global" key="repo{[sftp-setup-repo-id]}-sftp-host-user">{[sftp-setup-user]}</backrest-config-option>
|
||||
<backrest-config-option section="global" key="repo{[sftp-setup-repo-id]}-sftp-private-key-file">{[sftp-private-key-file]}</backrest-config-option>
|
||||
<backrest-config-option section="global" key="repo{[sftp-setup-repo-id]}-sftp-public-key-file">{[sftp-public-key-file]}</backrest-config-option>
|
||||
<backrest-config-option section="global" key="process-max">4</backrest-config-option>
|
||||
</backrest-config>
|
||||
|
||||
<p>When utilizing <proper>SFTP</proper>, if libssh2 is compiled against OpenSSH then <br-option>repo{[sftp-setup-repo-id]}-sftp-public-key-file</br-option> is optional.</p>
|
||||
|
||||
<execute-list host="{[host-pg1]}">
|
||||
<title>Generate ssh keypair for sftp backup</title>
|
||||
|
||||
<execute user="{[sftp-setup-user]}">
|
||||
<exe-cmd>ssh-keygen -f {[pg-home-path]}/.ssh/id_rsa_sftp
|
||||
-t rsa -b 4096 -N "" -m PEM</exe-cmd>
|
||||
</execute>
|
||||
</execute-list>
|
||||
|
||||
<execute-list host="{[host-sftp]}">
|
||||
<title>Copy <host>{[host-pg1]}</host> sftp backup public key to <host>{[host-sftp]}</host></title>
|
||||
|
||||
<execute user="{[sftp-setup-user]}">
|
||||
<exe-cmd>mkdir -m 750 -p {[pg-home-path]}/.ssh</exe-cmd>
|
||||
</execute>
|
||||
|
||||
<execute user="root" err-suppress="n" user-force="y">
|
||||
<exe-cmd>
|
||||
mkdir -m 750 -p /{[sftp-repo]} && chown {[sftp-setup-user]}:{[pg-group]} /{[sftp-repo]}
|
||||
</exe-cmd>
|
||||
</execute>
|
||||
|
||||
<execute user="root" err-suppress="y" user-force="y">
|
||||
<exe-cmd>
|
||||
(sudo ssh root@{[host-pg1]} cat {[pg-home-path]}/.ssh/id_rsa_sftp.pub) |
|
||||
sudo -u {[sftp-setup-user]} tee -a {[pg-home-path]}/.ssh/authorized_keys
|
||||
</exe-cmd>
|
||||
</execute>
|
||||
</execute-list>
|
||||
</block-define>
|
||||
|
||||
<!-- ======================================================================================================================= -->
|
||||
<section id="introduction">
|
||||
<title>Introduction</title>
|
||||
@ -783,6 +851,9 @@
|
||||
<!-- Create S3 server first to allow it time to boot before being used -->
|
||||
<host-add if="'{[s3-local]}' eq 'y'" id="{[host-s3-id]}" name="{[host-s3]}" user="root" image="{[s3-image]}" os="{[os-type]}" option="-v {[fake-cert-path]}/s3-server.crt:/root/.minio/certs/public.crt:ro -v {[fake-cert-path]}/s3-server.key:/root/.minio/certs/private.key:ro -e MINIO_REGION={[s3-region]} -e MINIO_DOMAIN={[s3-endpoint]} -e MINIO_BROWSER=off -e MINIO_ACCESS_KEY={[s3-key]} -e MINIO_SECRET_KEY={[s3-key-secret]}" param="server /data --address :443" update-hosts="n"/>
|
||||
|
||||
<!-- Create SFTP server first to allow it time to boot before being used -->
|
||||
<host-add id="{[host-sftp-id]}" name="{[host-sftp]}" user="{[host-sftp-user]}" image="{[host-sftp-image]}" os="{[os-type]}" mount="{[host-sftp-mount]}" option="{[host-mem]} {[host-option]}"/>
|
||||
|
||||
<p>This user guide is intended to be followed sequentially from beginning to end &mdash; each section depends on the last. For example, the <link section="/restore">Restore</link> section relies on setup that is performed in the <link section="/quickstart">Quick Start</link> section. Once <backrest/> is up and running then skipping around is possible but following the user guide in order is recommended the first time through.</p>
|
||||
|
||||
<p>Although the examples in this guide are targeted at <proper>{[user-guide-os]}</proper> and <postgres/> {[pg-version]}, it should be fairly easy to apply the examples to any Unix distribution and <postgres/> version. The only OS-specific commands are those to create, start, stop, and drop <postgres/> clusters. The <backrest/> commands will be the same on any Unix system though the location of the executable may vary. While <backrest/> strives to operate consistently across versions of <postgres/>, there are subtle differences between versions of <postgres/> that may show up in this guide when illustrating certain examples, e.g. <postgres/> path/file names and settings.</p>
|
||||
@ -925,15 +996,15 @@
|
||||
<execute if="{[os-type-is-debian]}" user="root" pre="y">
|
||||
<exe-cmd>
|
||||
apt-get install make gcc libpq-dev libssl-dev libxml2-dev pkg-config
|
||||
liblz4-dev libzstd-dev libbz2-dev libz-dev libyaml-dev
|
||||
liblz4-dev libzstd-dev libbz2-dev libz-dev libyaml-dev libssh2-1-dev
|
||||
</exe-cmd>
|
||||
<exe-cmd-extra>-y 2>&1</exe-cmd-extra>
|
||||
</execute>
|
||||
|
||||
<execute if="{[os-type-is-rhel]}" user="root" pre="y">
|
||||
<exe-cmd>
|
||||
yum install make gcc postgresql{[pg-version-nodot]}-devel
|
||||
openssl-devel libxml2-devel lz4-devel libzstd-devel bzip2-devel libyaml-devel
|
||||
yum install make gcc postgresql{[pg-version-nodot]}-devel openssl-devel
|
||||
libxml2-devel lz4-devel libzstd-devel bzip2-devel libyaml-devel libssh2-devel
|
||||
</exe-cmd>
|
||||
<exe-cmd-extra>-y 2>&1</exe-cmd-extra>
|
||||
</execute>
|
||||
@ -1147,6 +1218,19 @@
|
||||
</block>
|
||||
</section>
|
||||
|
||||
<!-- =================================================================================================================== -->
|
||||
<section id="sftp-support" if="'{[sftp-all]}' eq 'y'">
|
||||
<title>SFTP Storage Support</title>
|
||||
|
||||
<block id="sftp-setup">
|
||||
<block-variable-replace key="sftp-setup-repo-id">1</block-variable-replace>
|
||||
<block-variable-replace key="sftp-setup-host">{[host-pg1]}</block-variable-replace>
|
||||
<block-variable-replace key="sftp-setup-user">postgres</block-variable-replace>
|
||||
<block-variable-replace key="sftp-setup-config-owner">postgres:postgres</block-variable-replace>
|
||||
<block-variable-replace key="sftp-setup-user-home-path">{[pg-home-path]}</block-variable-replace>
|
||||
</block>
|
||||
</section>
|
||||
|
||||
<!-- =================================================================================================================== -->
|
||||
<section id="configure-archiving">
|
||||
<title>Configure Archiving</title>
|
||||
@ -2401,12 +2485,45 @@
|
||||
</execute-list>
|
||||
</section>
|
||||
|
||||
<!-- ======================================================================================================================= -->
|
||||
<section id="sftp-support" if="!{[object-any-all]}" depend="/quickstart/configure-archiving">
|
||||
<title>SFTP Support</title>
|
||||
|
||||
<block id="sftp-setup">
|
||||
<block-variable-replace key="sftp-setup-repo-id">4</block-variable-replace>
|
||||
<block-variable-replace key="sftp-setup-host">{[host-pg1]}</block-variable-replace>
|
||||
<block-variable-replace key="sftp-setup-user">postgres</block-variable-replace>
|
||||
<block-variable-replace key="sftp-setup-config-owner">postgres:postgres</block-variable-replace>
|
||||
</block>
|
||||
|
||||
<p>Commands are run exactly as if the repository were stored on a local disk.</p>
|
||||
|
||||
<execute-list host="{[host-pg1]}">
|
||||
<title>Create the stanza</title>
|
||||
|
||||
<execute user="postgres" output="y">
|
||||
<exe-cmd>{[project-exe]} {[dash]}-stanza={[postgres-cluster-demo]} {[dash]}-log-level-console=info stanza-create</exe-cmd>
|
||||
<exe-highlight>completed successfully</exe-highlight>
|
||||
</execute>
|
||||
</execute-list>
|
||||
|
||||
<execute-list host="{[host-pg1]}">
|
||||
<title>Backup the {[postgres-cluster-demo]} cluster</title>
|
||||
|
||||
<execute user="postgres" output="y">
|
||||
<exe-cmd>{[project-exe]} {[dash]}-stanza={[postgres-cluster-demo]} --repo=4
|
||||
--log-level-console=info backup</exe-cmd>
|
||||
<exe-highlight>no prior backup exists|full backup size</exe-highlight>
|
||||
</execute>
|
||||
</execute-list>
|
||||
</section>
|
||||
|
||||
<!-- ======================================================================================================================= -->
|
||||
<section id="gcs-support" if="!{[object-any-all]}" depend="/quickstart/configure-archiving">
|
||||
<title>GCS-Compatible Object Store Support</title>
|
||||
|
||||
<block id="gcs-setup">
|
||||
<block-variable-replace key="gcs-setup-repo-id">4</block-variable-replace>
|
||||
<block-variable-replace key="gcs-setup-repo-id">5</block-variable-replace>
|
||||
<block-variable-replace key="gcs-setup-host">{[host-pg1]}</block-variable-replace>
|
||||
<block-variable-replace key="gcs-setup-user">postgres</block-variable-replace>
|
||||
<block-variable-replace key="gcs-setup-config-owner">postgres:postgres</block-variable-replace>
|
||||
@ -2655,6 +2772,15 @@
|
||||
<block-variable-replace key="s3-setup-config-owner">{[br-user]}:{[br-group]}</block-variable-replace>
|
||||
<block-variable-replace key="s3-setup-create-bucket">n</block-variable-replace>
|
||||
</block>
|
||||
|
||||
<p if="'{[sftp-all]}' eq 'y'">Configure SFTP storage if required.</p>
|
||||
|
||||
<block id="sftp-setup" if="'{[sftp-all]}' eq 'y'">
|
||||
<block-variable-replace key="sftp-setup-repo-id">1</block-variable-replace>
|
||||
<block-variable-replace key="sftp-setup-host">{[host-repo1]}</block-variable-replace>
|
||||
<block-variable-replace key="sftp-setup-user">{[br-user]}</block-variable-replace>
|
||||
<block-variable-replace key="sftp-setup-config-owner">{[br-user]}:{[br-group]}</block-variable-replace>
|
||||
</block>
|
||||
</section>
|
||||
|
||||
<!-- =================================================================================================================== -->
|
||||
|
@ -165,6 +165,13 @@ lib_yaml = dependency('yaml-0.1')
|
||||
# Find required gz library
|
||||
lib_z = dependency('zlib')
|
||||
|
||||
# Find optional libssh2 library
|
||||
lib_ssh2 = dependency('libssh2', required: false)
|
||||
|
||||
if lib_ssh2.found()
|
||||
configuration.set('HAVE_LIBSSH2', true, description: 'Is libssh2 present?')
|
||||
endif
|
||||
|
||||
# Find optional zstd library
|
||||
lib_zstd = dependency('libzstd', version: '>=1.0', required: false)
|
||||
|
||||
|
@ -187,7 +187,11 @@ SRCS = \
|
||||
storage/s3/helper.c \
|
||||
storage/s3/read.c \
|
||||
storage/s3/storage.c \
|
||||
storage/s3/write.c
|
||||
storage/s3/write.c \
|
||||
storage/sftp/helper.c \
|
||||
storage/sftp/read.c \
|
||||
storage/sftp/storage.c \
|
||||
storage/sftp/write.c
|
||||
|
||||
####################################################################################################################################
|
||||
# Compiler options
|
||||
|
@ -23,6 +23,9 @@ Build Flags Generated by Configure
|
||||
// Is libzstd present?
|
||||
#undef HAVE_LIBZST
|
||||
|
||||
// Is libssh2 present?
|
||||
#undef HAVE_LIBSSH2
|
||||
|
||||
// Configuration path
|
||||
#undef CFGOPTDEF_CONFIG_PATH
|
||||
|
||||
|
@ -1735,6 +1735,7 @@ option:
|
||||
- gcs
|
||||
- posix
|
||||
- s3
|
||||
- sftp
|
||||
command:
|
||||
annotate:
|
||||
command-role:
|
||||
@ -2350,6 +2351,75 @@ option:
|
||||
command: repo-type
|
||||
depend: repo-s3-bucket
|
||||
|
||||
repo-sftp-host:
|
||||
section: global
|
||||
group: repo
|
||||
type: string
|
||||
command: repo-type
|
||||
depend:
|
||||
option: repo-type
|
||||
list:
|
||||
- sftp
|
||||
|
||||
repo-sftp-host-fingerprint:
|
||||
section: global
|
||||
group: repo
|
||||
type: string
|
||||
required: false
|
||||
command: repo-type
|
||||
depend: repo-sftp-host
|
||||
|
||||
repo-sftp-host-key-hash-type:
|
||||
section: global
|
||||
group: repo
|
||||
type: string-id
|
||||
allow-list:
|
||||
- md5
|
||||
- sha1
|
||||
- sha256
|
||||
command: repo-type
|
||||
depend: repo-sftp-host
|
||||
|
||||
repo-sftp-host-port:
|
||||
section: global
|
||||
group: repo
|
||||
type: integer
|
||||
default: 22
|
||||
allow-range: [1, 65535]
|
||||
command: repo-type
|
||||
depend: repo-sftp-host
|
||||
|
||||
repo-sftp-host-user:
|
||||
section: global
|
||||
group: repo
|
||||
type: string
|
||||
command: repo-type
|
||||
depend: repo-sftp-host
|
||||
|
||||
repo-sftp-private-key-file:
|
||||
section: global
|
||||
group: repo
|
||||
type: string
|
||||
command: repo-type
|
||||
depend: repo-sftp-host
|
||||
|
||||
repo-sftp-private-key-passphrase:
|
||||
section: global
|
||||
group: repo
|
||||
type: string
|
||||
required: false
|
||||
secure: true
|
||||
command: repo-type
|
||||
depend: repo-sftp-host
|
||||
|
||||
repo-sftp-public-key-file:
|
||||
section: global
|
||||
group: repo
|
||||
type: string
|
||||
required: false
|
||||
command: repo-type
|
||||
depend: repo-sftp-host
|
||||
|
||||
repo-storage-verify-tls:
|
||||
section: global
|
||||
group: repo
|
||||
|
@ -106,6 +106,17 @@ AC_CHECK_LIB(
|
||||
[AC_CHECK_HEADER(lz4frame.h, [AC_DEFINE(HAVE_LIBLZ4) AC_SUBST(LIBS, "${LIBS} -llz4")],
|
||||
[AC_MSG_ERROR([header file <lz4frame.h> is required])])])
|
||||
|
||||
# Check optional libSSH2 library
|
||||
# ----------------------------------------------------------------------------------------------------------------------------------
|
||||
AC_CHECK_LIB(
|
||||
[ssh2], [libssh2_init],
|
||||
[AC_CHECK_HEADER(libssh2.h, [AC_DEFINE(HAVE_LIBSSH2) AC_SUBST(LIBS, "${LIBS} -lssh2")],
|
||||
[AC_MSG_ERROR([header file <libssh2.h> is required])])])
|
||||
AC_CHECK_LIB(
|
||||
[ssh2], [libssh2_sftp_init],
|
||||
[AC_CHECK_HEADER(libssh2_sftp.h, [],
|
||||
[AC_MSG_ERROR([header file <libssh2_sftp.h> is required])])])
|
||||
|
||||
# Check optional zst library. Ignore any versions below 1.0.
|
||||
# ----------------------------------------------------------------------------------------------------------------------------------
|
||||
AC_CHECK_LIB(
|
||||
|
@ -1005,6 +1005,86 @@
|
||||
<example>path</example>
|
||||
</config-key>
|
||||
|
||||
<config-key id="repo-sftp-host" name="SFTP Repository Host">
|
||||
<summary>SFTP repository host.</summary>
|
||||
|
||||
<text>
|
||||
<p>The SFTP host containing the repository.</p>
|
||||
</text>
|
||||
|
||||
<example>sftprepo.domain</example>
|
||||
</config-key>
|
||||
|
||||
<config-key id="repo-sftp-host-fingerprint" name="SFTP Repository Host Fingerprint">
|
||||
<summary>SFTP repository host fingerprint.</summary>
|
||||
|
||||
<text>
|
||||
<p>SFTP repository host fingerprint generation should match the <setting>repo-sftp-host-key-hash-type</setting>. Generate the fingeprint via <code>awk '{print $2}' ssh_host_xxx_key.pub | base64 -d | (md5sum or sha1sum) -b</code>. The ssh host keys are normally found in the <path>/etc/ssh</path> directory.</p>
|
||||
</text>
|
||||
|
||||
<example>f84e172dfead7aeeeae6c1fdfb5aa8cf</example>
|
||||
</config-key>
|
||||
|
||||
<config-key id="repo-sftp-host-key-hash-type" name="SFTP Repository Host Key Hash Type">
|
||||
<summary>SFTP repository host key hash type.</summary>
|
||||
|
||||
<text>
|
||||
<p>SFTP repository host key hash type. Declares the hash type to be used to compute the digest of the remote system's host key on SSH startup. Newer versions of libssh2 support <id>sha256</id> in addition to md5 and sha1.</p>
|
||||
</text>
|
||||
|
||||
<example>sha256</example>
|
||||
</config-key>
|
||||
|
||||
<config-key id="repo-sftp-host-port" name="SFTP Repository Host Port">
|
||||
<summary>SFTP repository host port.</summary>
|
||||
|
||||
<text>
|
||||
<p>SFTP repository host port.</p>
|
||||
</text>
|
||||
|
||||
<example>22</example>
|
||||
</config-key>
|
||||
|
||||
<config-key id="repo-sftp-host-user" name="SFTP Repository Host User">
|
||||
<summary>SFTP repository host user.</summary>
|
||||
|
||||
<text>
|
||||
<p>User on the host used to store the repository.</p>
|
||||
</text>
|
||||
|
||||
<example>pg-backup</example>
|
||||
</config-key>
|
||||
|
||||
<config-key id="repo-sftp-private-key-file" name="SFTP Repository Private Key File">
|
||||
<summary>SFTP private key file.</summary>
|
||||
|
||||
<text>
|
||||
<p>SFTP private key file used for authentication.</p>
|
||||
</text>
|
||||
|
||||
<example>~/.ssh/id_ed25519</example>
|
||||
</config-key>
|
||||
|
||||
<config-key id="repo-sftp-private-key-passphrase" name="SFTP Repository Private Key Passphrase">
|
||||
<summary>SFTP private key passphrase.</summary>
|
||||
|
||||
<text>
|
||||
<p>Passphrase used to access the private key. This is an optional feature when creating an SSH public/private key pair.</p>
|
||||
</text>
|
||||
|
||||
<example>BeSureToGenerateAndUseASecurePassphrase</example>
|
||||
</config-key>
|
||||
|
||||
<config-key id="repo-sftp-public-key-file" name="SFTP Repository Public Key File">
|
||||
<summary>SFTP public key file.</summary>
|
||||
|
||||
<text>
|
||||
<p>SFTP public key file used for authentication. Optional if compiled against OpenSSL, required if compiled against a different library.</p>
|
||||
</text>
|
||||
|
||||
<example>~/.ssh/id_ed25519.pub</example>
|
||||
</config-key>
|
||||
|
||||
<config-key id="repo-storage-ca-file" name="Repository Storage CA File">
|
||||
<summary>Repository storage CA file.</summary>
|
||||
|
||||
@ -1089,6 +1169,7 @@
|
||||
<list-item><id>gcs</id> - Google Cloud Storage</list-item>
|
||||
<list-item><id>posix</id> - Posix-compliant file systems</list-item>
|
||||
<list-item><id>s3</id> - AWS Simple Storage Service</list-item>
|
||||
<list-item><id>sftp</id> - Secure File Transfer Protocol</list-item>
|
||||
</list>
|
||||
|
||||
<p>When an <proper>NFS</proper> mount is used as a <id>posix</id> repository, the same rules apply to <backrest/> as described in the <postgres/> documentation: <link url="https://www.postgresql.org/docs/current/creating-cluster.html#CREATING-CLUSTER-FILESYSTEM">Creating a Database Cluster - File Systems</link>.</p>
|
||||
|
@ -135,7 +135,7 @@ Option constants
|
||||
#define CFGOPT_TYPE "type"
|
||||
#define CFGOPT_VERBOSE "verbose"
|
||||
|
||||
#define CFG_OPTION_TOTAL 167
|
||||
#define CFG_OPTION_TOTAL 175
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Option value constants
|
||||
@ -269,6 +269,13 @@ Option value constants
|
||||
#define CFGOPTVAL_REPO_S3_URI_STYLE_PATH STRID5("path", 0x450300)
|
||||
#define CFGOPTVAL_REPO_S3_URI_STYLE_PATH_Z "path"
|
||||
|
||||
#define CFGOPTVAL_REPO_SFTP_HOST_KEY_HASH_TYPE_MD5 STRID5("md5", 0x748d0)
|
||||
#define CFGOPTVAL_REPO_SFTP_HOST_KEY_HASH_TYPE_MD5_Z "md5"
|
||||
#define CFGOPTVAL_REPO_SFTP_HOST_KEY_HASH_TYPE_SHA1 STRID6("sha1", 0x7412131)
|
||||
#define CFGOPTVAL_REPO_SFTP_HOST_KEY_HASH_TYPE_SHA1_Z "sha1"
|
||||
#define CFGOPTVAL_REPO_SFTP_HOST_KEY_HASH_TYPE_SHA256 STRID5("sha256", 0x3dde05130)
|
||||
#define CFGOPTVAL_REPO_SFTP_HOST_KEY_HASH_TYPE_SHA256_Z "sha256"
|
||||
|
||||
#define CFGOPTVAL_REPO_TYPE_AZURE STRID5("azure", 0x5957410)
|
||||
#define CFGOPTVAL_REPO_TYPE_AZURE_Z "azure"
|
||||
#define CFGOPTVAL_REPO_TYPE_CIFS STRID5("cifs", 0x999230)
|
||||
@ -279,6 +286,8 @@ Option value constants
|
||||
#define CFGOPTVAL_REPO_TYPE_POSIX_Z "posix"
|
||||
#define CFGOPTVAL_REPO_TYPE_S3 STRID6("s3", 0x7d31)
|
||||
#define CFGOPTVAL_REPO_TYPE_S3_Z "s3"
|
||||
#define CFGOPTVAL_REPO_TYPE_SFTP STRID5("sftp", 0x850d30)
|
||||
#define CFGOPTVAL_REPO_TYPE_SFTP_Z "sftp"
|
||||
|
||||
#define CFGOPTVAL_SORT_ASC STRID5("asc", 0xe610)
|
||||
#define CFGOPTVAL_SORT_ASC_Z "asc"
|
||||
@ -499,6 +508,14 @@ typedef enum
|
||||
cfgOptRepoS3Role,
|
||||
cfgOptRepoS3Token,
|
||||
cfgOptRepoS3UriStyle,
|
||||
cfgOptRepoSftpHost,
|
||||
cfgOptRepoSftpHostFingerprint,
|
||||
cfgOptRepoSftpHostKeyHashType,
|
||||
cfgOptRepoSftpHostPort,
|
||||
cfgOptRepoSftpHostUser,
|
||||
cfgOptRepoSftpPrivateKeyFile,
|
||||
cfgOptRepoSftpPrivateKeyPassphrase,
|
||||
cfgOptRepoSftpPublicKeyFile,
|
||||
cfgOptRepoStorageCaFile,
|
||||
cfgOptRepoStorageCaPath,
|
||||
cfgOptRepoStorageHost,
|
||||
|
@ -23,6 +23,7 @@ Configuration Load
|
||||
#include "storage/cifs/storage.h"
|
||||
#include "storage/helper.h"
|
||||
#include "storage/posix/storage.h"
|
||||
#include "storage/sftp/storage.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Load log settings
|
||||
@ -86,9 +87,10 @@ cfgLoadUpdateOption(void)
|
||||
{
|
||||
for (unsigned int optionIdx = 0; optionIdx < cfgOptionGroupIdxTotal(cfgOptGrpRepo); optionIdx++)
|
||||
{
|
||||
// If the repo is local and either posix or cifs
|
||||
// If the repo is local and either posix, cifs or sftp
|
||||
if (!cfgOptionIdxTest(cfgOptRepoHost, optionIdx) &&
|
||||
(cfgOptionIdxStrId(cfgOptRepoType, optionIdx) == STORAGE_POSIX_TYPE ||
|
||||
cfgOptionIdxStrId(cfgOptRepoType, optionIdx) == STORAGE_SFTP_TYPE ||
|
||||
cfgOptionIdxStrId(cfgOptRepoType, optionIdx) == STORAGE_CIFS_TYPE))
|
||||
{
|
||||
// Ensure a local repo does not have the same path as another local repo of the same type
|
||||
|
@ -24,6 +24,7 @@ static const StringPub parseRuleValueStr[] =
|
||||
PARSE_RULE_STRPUB("1MiB"), // val/str
|
||||
PARSE_RULE_STRPUB("2"), // val/str
|
||||
PARSE_RULE_STRPUB("20MiB"), // val/str
|
||||
PARSE_RULE_STRPUB("22"), // val/str
|
||||
PARSE_RULE_STRPUB("256KiB"), // val/str
|
||||
PARSE_RULE_STRPUB("2MiB"), // val/str
|
||||
PARSE_RULE_STRPUB("3"), // val/str
|
||||
@ -74,6 +75,7 @@ typedef enum
|
||||
parseRuleValStrQT_1MiB_QT, // val/str/enum
|
||||
parseRuleValStrQT_2_QT, // val/str/enum
|
||||
parseRuleValStrQT_20MiB_QT, // val/str/enum
|
||||
parseRuleValStrQT_22_QT, // val/str/enum
|
||||
parseRuleValStrQT_256KiB_QT, // val/str/enum
|
||||
parseRuleValStrQT_2MiB_QT, // val/str/enum
|
||||
parseRuleValStrQT_3_QT, // val/str/enum
|
||||
@ -139,6 +141,7 @@ static const StringId parseRuleValueStrId[] =
|
||||
STRID5("json", 0x73e6a0), // val/strid
|
||||
STRID5("lsn", 0x3a6c0), // val/strid
|
||||
STRID6("lz4", 0x2068c1), // val/strid
|
||||
STRID5("md5", 0x748d0), // val/strid
|
||||
STRID5("name", 0x2b42e0), // val/strid
|
||||
STRID5("none", 0x2b9ee0), // val/strid
|
||||
STRID5("off", 0x18cf0), // val/strid
|
||||
@ -152,6 +155,9 @@ static const StringId parseRuleValueStrId[] =
|
||||
STRID6("s3", 0x7d31), // val/strid
|
||||
STRID5("sas", 0x4c330), // val/strid
|
||||
STRID5("service", 0x1469b48b30), // val/strid
|
||||
STRID5("sftp", 0x850d30), // val/strid
|
||||
STRID6("sha1", 0x7412131), // val/strid
|
||||
STRID5("sha256", 0x3dde05130), // val/strid
|
||||
STRID5("shared", 0x85905130), // val/strid
|
||||
STRID5("shutdown", 0x75de4a55130), // val/strid
|
||||
STRID5("ssh", 0x22730), // val/strid
|
||||
@ -192,6 +198,7 @@ typedef enum
|
||||
parseRuleValStrIdJson, // val/strid/enum
|
||||
parseRuleValStrIdLsn, // val/strid/enum
|
||||
parseRuleValStrIdLz4, // val/strid/enum
|
||||
parseRuleValStrIdMd5, // val/strid/enum
|
||||
parseRuleValStrIdName, // val/strid/enum
|
||||
parseRuleValStrIdNone, // val/strid/enum
|
||||
parseRuleValStrIdOff, // val/strid/enum
|
||||
@ -205,6 +212,9 @@ typedef enum
|
||||
parseRuleValStrIdS3, // val/strid/enum
|
||||
parseRuleValStrIdSas, // val/strid/enum
|
||||
parseRuleValStrIdService, // val/strid/enum
|
||||
parseRuleValStrIdSftp, // val/strid/enum
|
||||
parseRuleValStrIdSha1, // val/strid/enum
|
||||
parseRuleValStrIdSha256, // val/strid/enum
|
||||
parseRuleValStrIdShared, // val/strid/enum
|
||||
parseRuleValStrIdShutdown, // val/strid/enum
|
||||
parseRuleValStrIdSsh, // val/strid/enum
|
||||
@ -232,6 +242,7 @@ static const int64_t parseRuleValueInt[] =
|
||||
2, // val/int
|
||||
3, // val/int
|
||||
9, // val/int
|
||||
22, // val/int
|
||||
32, // val/int
|
||||
100, // val/int
|
||||
256, // val/int
|
||||
@ -281,6 +292,7 @@ typedef enum
|
||||
parseRuleValInt2, // val/int/enum
|
||||
parseRuleValInt3, // val/int/enum
|
||||
parseRuleValInt9, // val/int/enum
|
||||
parseRuleValInt22, // val/int/enum
|
||||
parseRuleValInt32, // val/int/enum
|
||||
parseRuleValInt100, // val/int/enum
|
||||
parseRuleValInt256, // val/int/enum
|
||||
@ -7857,6 +7869,666 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] =
|
||||
), // opt/repo-s3-uri-style
|
||||
), // opt/repo-s3-uri-style
|
||||
// -----------------------------------------------------------------------------------------------------------------------------
|
||||
PARSE_RULE_OPTION // opt/repo-sftp-host
|
||||
( // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_NAME("repo-sftp-host"), // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_TYPE(cfgOptTypeString), // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_RESET(true), // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_REQUIRED(true), // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_SECTION(cfgSectionGlobal), // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_GROUP_MEMBER(true), // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_GROUP_ID(cfgOptGrpRepo), // opt/repo-sftp-host
|
||||
// opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_COMMAND_ROLE_MAIN_VALID_LIST // opt/repo-sftp-host
|
||||
( // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdAnnotate) // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdBackup) // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdCheck) // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdExpire) // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdInfo) // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdManifest) // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoCreate) // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoGet) // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoLs) // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoPut) // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoRm) // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRestore) // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaCreate) // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaDelete) // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaUpgrade) // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdVerify) // opt/repo-sftp-host
|
||||
), // opt/repo-sftp-host
|
||||
// opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_COMMAND_ROLE_ASYNC_VALID_LIST // opt/repo-sftp-host
|
||||
( // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-host
|
||||
), // opt/repo-sftp-host
|
||||
// opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_COMMAND_ROLE_LOCAL_VALID_LIST // opt/repo-sftp-host
|
||||
( // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdBackup) // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRestore) // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdVerify) // opt/repo-sftp-host
|
||||
), // opt/repo-sftp-host
|
||||
// opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_COMMAND_ROLE_REMOTE_VALID_LIST // opt/repo-sftp-host
|
||||
( // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdAnnotate) // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdCheck) // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdInfo) // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdManifest) // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoCreate) // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoGet) // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoLs) // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoPut) // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoRm) // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRestore) // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaCreate) // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaDelete) // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaUpgrade) // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdVerify) // opt/repo-sftp-host
|
||||
), // opt/repo-sftp-host
|
||||
// opt/repo-sftp-host
|
||||
PARSE_RULE_OPTIONAL // opt/repo-sftp-host
|
||||
( // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTIONAL_GROUP // opt/repo-sftp-host
|
||||
( // opt/repo-sftp-host
|
||||
PARSE_RULE_OPTIONAL_DEPEND // opt/repo-sftp-host
|
||||
( // opt/repo-sftp-host
|
||||
PARSE_RULE_VAL_OPT(cfgOptRepoType), // opt/repo-sftp-host
|
||||
PARSE_RULE_VAL_STRID(parseRuleValStrIdSftp), // opt/repo-sftp-host
|
||||
), // opt/repo-sftp-host
|
||||
), // opt/repo-sftp-host
|
||||
), // opt/repo-sftp-host
|
||||
), // opt/repo-sftp-host
|
||||
// -----------------------------------------------------------------------------------------------------------------------------
|
||||
PARSE_RULE_OPTION // opt/repo-sftp-host-fingerprint
|
||||
( // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_NAME("repo-sftp-host-fingerprint"), // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_TYPE(cfgOptTypeString), // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_RESET(true), // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_REQUIRED(false), // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_SECTION(cfgSectionGlobal), // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_GROUP_MEMBER(true), // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_GROUP_ID(cfgOptGrpRepo), // opt/repo-sftp-host-fingerprint
|
||||
// opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_COMMAND_ROLE_MAIN_VALID_LIST // opt/repo-sftp-host-fingerprint
|
||||
( // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdAnnotate) // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdBackup) // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdCheck) // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdExpire) // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdInfo) // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdManifest) // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoCreate) // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoGet) // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoLs) // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoPut) // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoRm) // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRestore) // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaCreate) // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaDelete) // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaUpgrade) // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdVerify) // opt/repo-sftp-host-fingerprint
|
||||
), // opt/repo-sftp-host-fingerprint
|
||||
// opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_COMMAND_ROLE_ASYNC_VALID_LIST // opt/repo-sftp-host-fingerprint
|
||||
( // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-host-fingerprint
|
||||
), // opt/repo-sftp-host-fingerprint
|
||||
// opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_COMMAND_ROLE_LOCAL_VALID_LIST // opt/repo-sftp-host-fingerprint
|
||||
( // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdBackup) // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRestore) // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdVerify) // opt/repo-sftp-host-fingerprint
|
||||
), // opt/repo-sftp-host-fingerprint
|
||||
// opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_COMMAND_ROLE_REMOTE_VALID_LIST // opt/repo-sftp-host-fingerprint
|
||||
( // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdAnnotate) // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdCheck) // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdInfo) // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdManifest) // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoCreate) // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoGet) // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoLs) // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoPut) // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoRm) // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRestore) // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaCreate) // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaDelete) // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaUpgrade) // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdVerify) // opt/repo-sftp-host-fingerprint
|
||||
), // opt/repo-sftp-host-fingerprint
|
||||
// opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTIONAL // opt/repo-sftp-host-fingerprint
|
||||
( // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTIONAL_GROUP // opt/repo-sftp-host-fingerprint
|
||||
( // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_OPTIONAL_DEPEND // opt/repo-sftp-host-fingerprint
|
||||
( // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_VAL_OPT(cfgOptRepoType), // opt/repo-sftp-host-fingerprint
|
||||
PARSE_RULE_VAL_STRID(parseRuleValStrIdSftp), // opt/repo-sftp-host-fingerprint
|
||||
), // opt/repo-sftp-host-fingerprint
|
||||
), // opt/repo-sftp-host-fingerprint
|
||||
), // opt/repo-sftp-host-fingerprint
|
||||
), // opt/repo-sftp-host-fingerprint
|
||||
// -----------------------------------------------------------------------------------------------------------------------------
|
||||
PARSE_RULE_OPTION // opt/repo-sftp-host-key-hash-type
|
||||
( // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_NAME("repo-sftp-host-key-hash-type"), // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_TYPE(cfgOptTypeStringId), // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_RESET(true), // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_REQUIRED(true), // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_SECTION(cfgSectionGlobal), // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_GROUP_MEMBER(true), // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_GROUP_ID(cfgOptGrpRepo), // opt/repo-sftp-host-key-hash-type
|
||||
// opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_COMMAND_ROLE_MAIN_VALID_LIST // opt/repo-sftp-host-key-hash-type
|
||||
( // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdAnnotate) // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdBackup) // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdCheck) // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdExpire) // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdInfo) // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdManifest) // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoCreate) // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoGet) // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoLs) // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoPut) // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoRm) // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRestore) // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaCreate) // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaDelete) // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaUpgrade) // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdVerify) // opt/repo-sftp-host-key-hash-type
|
||||
), // opt/repo-sftp-host-key-hash-type
|
||||
// opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_COMMAND_ROLE_ASYNC_VALID_LIST // opt/repo-sftp-host-key-hash-type
|
||||
( // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-host-key-hash-type
|
||||
), // opt/repo-sftp-host-key-hash-type
|
||||
// opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_COMMAND_ROLE_LOCAL_VALID_LIST // opt/repo-sftp-host-key-hash-type
|
||||
( // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdBackup) // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRestore) // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdVerify) // opt/repo-sftp-host-key-hash-type
|
||||
), // opt/repo-sftp-host-key-hash-type
|
||||
// opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_COMMAND_ROLE_REMOTE_VALID_LIST // opt/repo-sftp-host-key-hash-type
|
||||
( // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdAnnotate) // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdCheck) // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdInfo) // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdManifest) // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoCreate) // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoGet) // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoLs) // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoPut) // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoRm) // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRestore) // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaCreate) // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaDelete) // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaUpgrade) // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdVerify) // opt/repo-sftp-host-key-hash-type
|
||||
), // opt/repo-sftp-host-key-hash-type
|
||||
// opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTIONAL // opt/repo-sftp-host-key-hash-type
|
||||
( // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTIONAL_GROUP // opt/repo-sftp-host-key-hash-type
|
||||
( // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTIONAL_DEPEND // opt/repo-sftp-host-key-hash-type
|
||||
( // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_VAL_OPT(cfgOptRepoType), // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_VAL_STRID(parseRuleValStrIdSftp), // opt/repo-sftp-host-key-hash-type
|
||||
), // opt/repo-sftp-host-key-hash-type
|
||||
// opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_OPTIONAL_ALLOW_LIST // opt/repo-sftp-host-key-hash-type
|
||||
( // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_VAL_STRID(parseRuleValStrIdMd5), // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_VAL_STRID(parseRuleValStrIdSha1), // opt/repo-sftp-host-key-hash-type
|
||||
PARSE_RULE_VAL_STRID(parseRuleValStrIdSha256), // opt/repo-sftp-host-key-hash-type
|
||||
), // opt/repo-sftp-host-key-hash-type
|
||||
), // opt/repo-sftp-host-key-hash-type
|
||||
), // opt/repo-sftp-host-key-hash-type
|
||||
), // opt/repo-sftp-host-key-hash-type
|
||||
// -----------------------------------------------------------------------------------------------------------------------------
|
||||
PARSE_RULE_OPTION // opt/repo-sftp-host-port
|
||||
( // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_NAME("repo-sftp-host-port"), // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_TYPE(cfgOptTypeInteger), // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_RESET(true), // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_REQUIRED(true), // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_SECTION(cfgSectionGlobal), // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_GROUP_MEMBER(true), // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_GROUP_ID(cfgOptGrpRepo), // opt/repo-sftp-host-port
|
||||
// opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_COMMAND_ROLE_MAIN_VALID_LIST // opt/repo-sftp-host-port
|
||||
( // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdAnnotate) // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdBackup) // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdCheck) // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdExpire) // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdInfo) // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdManifest) // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoCreate) // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoGet) // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoLs) // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoPut) // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoRm) // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRestore) // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaCreate) // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaDelete) // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaUpgrade) // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdVerify) // opt/repo-sftp-host-port
|
||||
), // opt/repo-sftp-host-port
|
||||
// opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_COMMAND_ROLE_ASYNC_VALID_LIST // opt/repo-sftp-host-port
|
||||
( // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-host-port
|
||||
), // opt/repo-sftp-host-port
|
||||
// opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_COMMAND_ROLE_LOCAL_VALID_LIST // opt/repo-sftp-host-port
|
||||
( // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdBackup) // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRestore) // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdVerify) // opt/repo-sftp-host-port
|
||||
), // opt/repo-sftp-host-port
|
||||
// opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_COMMAND_ROLE_REMOTE_VALID_LIST // opt/repo-sftp-host-port
|
||||
( // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdAnnotate) // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdCheck) // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdInfo) // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdManifest) // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoCreate) // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoGet) // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoLs) // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoPut) // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoRm) // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRestore) // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaCreate) // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaDelete) // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaUpgrade) // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdVerify) // opt/repo-sftp-host-port
|
||||
), // opt/repo-sftp-host-port
|
||||
// opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTIONAL // opt/repo-sftp-host-port
|
||||
( // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTIONAL_GROUP // opt/repo-sftp-host-port
|
||||
( // opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTIONAL_DEPEND // opt/repo-sftp-host-port
|
||||
( // opt/repo-sftp-host-port
|
||||
PARSE_RULE_VAL_OPT(cfgOptRepoType), // opt/repo-sftp-host-port
|
||||
PARSE_RULE_VAL_STRID(parseRuleValStrIdSftp), // opt/repo-sftp-host-port
|
||||
), // opt/repo-sftp-host-port
|
||||
// opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTIONAL_ALLOW_RANGE // opt/repo-sftp-host-port
|
||||
( // opt/repo-sftp-host-port
|
||||
PARSE_RULE_VAL_INT(parseRuleValInt1), // opt/repo-sftp-host-port
|
||||
PARSE_RULE_VAL_INT(parseRuleValInt65535), // opt/repo-sftp-host-port
|
||||
), // opt/repo-sftp-host-port
|
||||
// opt/repo-sftp-host-port
|
||||
PARSE_RULE_OPTIONAL_DEFAULT // opt/repo-sftp-host-port
|
||||
( // opt/repo-sftp-host-port
|
||||
PARSE_RULE_VAL_INT(parseRuleValInt22), // opt/repo-sftp-host-port
|
||||
PARSE_RULE_VAL_STR(parseRuleValStrQT_22_QT), // opt/repo-sftp-host-port
|
||||
), // opt/repo-sftp-host-port
|
||||
), // opt/repo-sftp-host-port
|
||||
), // opt/repo-sftp-host-port
|
||||
), // opt/repo-sftp-host-port
|
||||
// -----------------------------------------------------------------------------------------------------------------------------
|
||||
PARSE_RULE_OPTION // opt/repo-sftp-host-user
|
||||
( // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_NAME("repo-sftp-host-user"), // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_TYPE(cfgOptTypeString), // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_RESET(true), // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_REQUIRED(true), // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_SECTION(cfgSectionGlobal), // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_GROUP_MEMBER(true), // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_GROUP_ID(cfgOptGrpRepo), // opt/repo-sftp-host-user
|
||||
// opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_COMMAND_ROLE_MAIN_VALID_LIST // opt/repo-sftp-host-user
|
||||
( // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdAnnotate) // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdBackup) // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdCheck) // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdExpire) // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdInfo) // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdManifest) // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoCreate) // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoGet) // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoLs) // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoPut) // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoRm) // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRestore) // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaCreate) // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaDelete) // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaUpgrade) // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdVerify) // opt/repo-sftp-host-user
|
||||
), // opt/repo-sftp-host-user
|
||||
// opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_COMMAND_ROLE_ASYNC_VALID_LIST // opt/repo-sftp-host-user
|
||||
( // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-host-user
|
||||
), // opt/repo-sftp-host-user
|
||||
// opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_COMMAND_ROLE_LOCAL_VALID_LIST // opt/repo-sftp-host-user
|
||||
( // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdBackup) // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRestore) // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdVerify) // opt/repo-sftp-host-user
|
||||
), // opt/repo-sftp-host-user
|
||||
// opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_COMMAND_ROLE_REMOTE_VALID_LIST // opt/repo-sftp-host-user
|
||||
( // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdAnnotate) // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdCheck) // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdInfo) // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdManifest) // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoCreate) // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoGet) // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoLs) // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoPut) // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoRm) // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRestore) // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaCreate) // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaDelete) // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaUpgrade) // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdVerify) // opt/repo-sftp-host-user
|
||||
), // opt/repo-sftp-host-user
|
||||
// opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTIONAL // opt/repo-sftp-host-user
|
||||
( // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTIONAL_GROUP // opt/repo-sftp-host-user
|
||||
( // opt/repo-sftp-host-user
|
||||
PARSE_RULE_OPTIONAL_DEPEND // opt/repo-sftp-host-user
|
||||
( // opt/repo-sftp-host-user
|
||||
PARSE_RULE_VAL_OPT(cfgOptRepoType), // opt/repo-sftp-host-user
|
||||
PARSE_RULE_VAL_STRID(parseRuleValStrIdSftp), // opt/repo-sftp-host-user
|
||||
), // opt/repo-sftp-host-user
|
||||
), // opt/repo-sftp-host-user
|
||||
), // opt/repo-sftp-host-user
|
||||
), // opt/repo-sftp-host-user
|
||||
// -----------------------------------------------------------------------------------------------------------------------------
|
||||
PARSE_RULE_OPTION // opt/repo-sftp-private-key-file
|
||||
( // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_NAME("repo-sftp-private-key-file"), // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_TYPE(cfgOptTypeString), // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_RESET(true), // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_REQUIRED(true), // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_SECTION(cfgSectionGlobal), // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_GROUP_MEMBER(true), // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_GROUP_ID(cfgOptGrpRepo), // opt/repo-sftp-private-key-file
|
||||
// opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_COMMAND_ROLE_MAIN_VALID_LIST // opt/repo-sftp-private-key-file
|
||||
( // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdAnnotate) // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdBackup) // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdCheck) // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdExpire) // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdInfo) // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdManifest) // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoCreate) // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoGet) // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoLs) // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoPut) // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoRm) // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRestore) // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaCreate) // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaDelete) // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaUpgrade) // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdVerify) // opt/repo-sftp-private-key-file
|
||||
), // opt/repo-sftp-private-key-file
|
||||
// opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_COMMAND_ROLE_ASYNC_VALID_LIST // opt/repo-sftp-private-key-file
|
||||
( // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-private-key-file
|
||||
), // opt/repo-sftp-private-key-file
|
||||
// opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_COMMAND_ROLE_LOCAL_VALID_LIST // opt/repo-sftp-private-key-file
|
||||
( // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdBackup) // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRestore) // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdVerify) // opt/repo-sftp-private-key-file
|
||||
), // opt/repo-sftp-private-key-file
|
||||
// opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_COMMAND_ROLE_REMOTE_VALID_LIST // opt/repo-sftp-private-key-file
|
||||
( // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdAnnotate) // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdCheck) // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdInfo) // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdManifest) // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoCreate) // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoGet) // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoLs) // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoPut) // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoRm) // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRestore) // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaCreate) // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaDelete) // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaUpgrade) // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdVerify) // opt/repo-sftp-private-key-file
|
||||
), // opt/repo-sftp-private-key-file
|
||||
// opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTIONAL // opt/repo-sftp-private-key-file
|
||||
( // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTIONAL_GROUP // opt/repo-sftp-private-key-file
|
||||
( // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_OPTIONAL_DEPEND // opt/repo-sftp-private-key-file
|
||||
( // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_VAL_OPT(cfgOptRepoType), // opt/repo-sftp-private-key-file
|
||||
PARSE_RULE_VAL_STRID(parseRuleValStrIdSftp), // opt/repo-sftp-private-key-file
|
||||
), // opt/repo-sftp-private-key-file
|
||||
), // opt/repo-sftp-private-key-file
|
||||
), // opt/repo-sftp-private-key-file
|
||||
), // opt/repo-sftp-private-key-file
|
||||
// -----------------------------------------------------------------------------------------------------------------------------
|
||||
PARSE_RULE_OPTION // opt/repo-sftp-private-key-passphrase
|
||||
( // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_NAME("repo-sftp-private-key-passphrase"), // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_TYPE(cfgOptTypeString), // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_RESET(true), // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_REQUIRED(false), // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_SECTION(cfgSectionGlobal), // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_SECURE(true), // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_GROUP_MEMBER(true), // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_GROUP_ID(cfgOptGrpRepo), // opt/repo-sftp-private-key-passphrase
|
||||
// opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_COMMAND_ROLE_MAIN_VALID_LIST // opt/repo-sftp-private-key-passphrase
|
||||
( // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdAnnotate) // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdBackup) // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdCheck) // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdExpire) // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdInfo) // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdManifest) // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoCreate) // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoGet) // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoLs) // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoPut) // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoRm) // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRestore) // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaCreate) // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaDelete) // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaUpgrade) // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdVerify) // opt/repo-sftp-private-key-passphrase
|
||||
), // opt/repo-sftp-private-key-passphrase
|
||||
// opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_COMMAND_ROLE_ASYNC_VALID_LIST // opt/repo-sftp-private-key-passphrase
|
||||
( // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-private-key-passphrase
|
||||
), // opt/repo-sftp-private-key-passphrase
|
||||
// opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_COMMAND_ROLE_LOCAL_VALID_LIST // opt/repo-sftp-private-key-passphrase
|
||||
( // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdBackup) // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRestore) // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdVerify) // opt/repo-sftp-private-key-passphrase
|
||||
), // opt/repo-sftp-private-key-passphrase
|
||||
// opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_COMMAND_ROLE_REMOTE_VALID_LIST // opt/repo-sftp-private-key-passphrase
|
||||
( // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdAnnotate) // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdCheck) // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdInfo) // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdManifest) // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoCreate) // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoGet) // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoLs) // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoPut) // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoRm) // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRestore) // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaCreate) // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaDelete) // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaUpgrade) // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdVerify) // opt/repo-sftp-private-key-passphrase
|
||||
), // opt/repo-sftp-private-key-passphrase
|
||||
// opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTIONAL // opt/repo-sftp-private-key-passphrase
|
||||
( // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTIONAL_GROUP // opt/repo-sftp-private-key-passphrase
|
||||
( // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_OPTIONAL_DEPEND // opt/repo-sftp-private-key-passphrase
|
||||
( // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_VAL_OPT(cfgOptRepoType), // opt/repo-sftp-private-key-passphrase
|
||||
PARSE_RULE_VAL_STRID(parseRuleValStrIdSftp), // opt/repo-sftp-private-key-passphrase
|
||||
), // opt/repo-sftp-private-key-passphrase
|
||||
), // opt/repo-sftp-private-key-passphrase
|
||||
), // opt/repo-sftp-private-key-passphrase
|
||||
), // opt/repo-sftp-private-key-passphrase
|
||||
// -----------------------------------------------------------------------------------------------------------------------------
|
||||
PARSE_RULE_OPTION // opt/repo-sftp-public-key-file
|
||||
( // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_NAME("repo-sftp-public-key-file"), // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_TYPE(cfgOptTypeString), // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_RESET(true), // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_REQUIRED(false), // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_SECTION(cfgSectionGlobal), // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_GROUP_MEMBER(true), // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_GROUP_ID(cfgOptGrpRepo), // opt/repo-sftp-public-key-file
|
||||
// opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_COMMAND_ROLE_MAIN_VALID_LIST // opt/repo-sftp-public-key-file
|
||||
( // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdAnnotate) // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdBackup) // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdCheck) // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdExpire) // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdInfo) // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdManifest) // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoCreate) // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoGet) // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoLs) // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoPut) // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoRm) // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRestore) // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaCreate) // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaDelete) // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaUpgrade) // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdVerify) // opt/repo-sftp-public-key-file
|
||||
), // opt/repo-sftp-public-key-file
|
||||
// opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_COMMAND_ROLE_ASYNC_VALID_LIST // opt/repo-sftp-public-key-file
|
||||
( // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-public-key-file
|
||||
), // opt/repo-sftp-public-key-file
|
||||
// opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_COMMAND_ROLE_LOCAL_VALID_LIST // opt/repo-sftp-public-key-file
|
||||
( // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdBackup) // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRestore) // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdVerify) // opt/repo-sftp-public-key-file
|
||||
), // opt/repo-sftp-public-key-file
|
||||
// opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_COMMAND_ROLE_REMOTE_VALID_LIST // opt/repo-sftp-public-key-file
|
||||
( // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdAnnotate) // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchiveGet) // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdArchivePush) // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdCheck) // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdInfo) // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdManifest) // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoCreate) // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoGet) // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoLs) // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoPut) // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRepoRm) // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdRestore) // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaCreate) // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaDelete) // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdStanzaUpgrade) // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTION_COMMAND(cfgCmdVerify) // opt/repo-sftp-public-key-file
|
||||
), // opt/repo-sftp-public-key-file
|
||||
// opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTIONAL // opt/repo-sftp-public-key-file
|
||||
( // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTIONAL_GROUP // opt/repo-sftp-public-key-file
|
||||
( // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_OPTIONAL_DEPEND // opt/repo-sftp-public-key-file
|
||||
( // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_VAL_OPT(cfgOptRepoType), // opt/repo-sftp-public-key-file
|
||||
PARSE_RULE_VAL_STRID(parseRuleValStrIdSftp), // opt/repo-sftp-public-key-file
|
||||
), // opt/repo-sftp-public-key-file
|
||||
), // opt/repo-sftp-public-key-file
|
||||
), // opt/repo-sftp-public-key-file
|
||||
), // opt/repo-sftp-public-key-file
|
||||
// -----------------------------------------------------------------------------------------------------------------------------
|
||||
PARSE_RULE_OPTION // opt/repo-storage-ca-file
|
||||
( // opt/repo-storage-ca-file
|
||||
PARSE_RULE_OPTION_NAME("repo-storage-ca-file"), // opt/repo-storage-ca-file
|
||||
@ -8450,6 +9122,7 @@ static const ParseRuleOption parseRuleOption[CFG_OPTION_TOTAL] =
|
||||
PARSE_RULE_VAL_STRID(parseRuleValStrIdGcs), // opt/repo-type
|
||||
PARSE_RULE_VAL_STRID(parseRuleValStrIdPosix), // opt/repo-type
|
||||
PARSE_RULE_VAL_STRID(parseRuleValStrIdS3), // opt/repo-type
|
||||
PARSE_RULE_VAL_STRID(parseRuleValStrIdSftp), // opt/repo-type
|
||||
), // opt/repo-type
|
||||
// opt/repo-type
|
||||
PARSE_RULE_OPTIONAL_DEFAULT // opt/repo-type
|
||||
@ -9990,6 +10663,14 @@ static const uint8_t optionResolveOrder[] =
|
||||
cfgOptRepoS3Role, // opt-resolve-order
|
||||
cfgOptRepoS3Token, // opt-resolve-order
|
||||
cfgOptRepoS3UriStyle, // opt-resolve-order
|
||||
cfgOptRepoSftpHost, // opt-resolve-order
|
||||
cfgOptRepoSftpHostFingerprint, // opt-resolve-order
|
||||
cfgOptRepoSftpHostKeyHashType, // opt-resolve-order
|
||||
cfgOptRepoSftpHostPort, // opt-resolve-order
|
||||
cfgOptRepoSftpHostUser, // opt-resolve-order
|
||||
cfgOptRepoSftpPrivateKeyFile, // opt-resolve-order
|
||||
cfgOptRepoSftpPrivateKeyPassphrase, // opt-resolve-order
|
||||
cfgOptRepoSftpPublicKeyFile, // opt-resolve-order
|
||||
cfgOptRepoStorageCaFile, // opt-resolve-order
|
||||
cfgOptRepoStorageCaPath, // opt-resolve-order
|
||||
cfgOptRepoStorageHost, // opt-resolve-order
|
||||
|
101
src/configure
vendored
101
src/configure
vendored
@ -4034,6 +4034,105 @@ fi
|
||||
fi
|
||||
|
||||
|
||||
# Check optional libSSH2 library
|
||||
# ----------------------------------------------------------------------------------------------------------------------------------
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libssh2_init in -lssh2" >&5
|
||||
printf %s "checking for libssh2_init in -lssh2... " >&6; }
|
||||
if test ${ac_cv_lib_ssh2_libssh2_init+y}
|
||||
then :
|
||||
printf %s "(cached) " >&6
|
||||
else $as_nop
|
||||
ac_check_lib_save_LIBS=$LIBS
|
||||
LIBS="-lssh2 $LIBS"
|
||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
|
||||
/* Override any GCC internal prototype to avoid an error.
|
||||
Use char because int might match the return type of a GCC
|
||||
builtin and then its argument prototype would still apply. */
|
||||
char libssh2_init ();
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
return libssh2_init ();
|
||||
;
|
||||
return 0;
|
||||
}
|
||||
_ACEOF
|
||||
if ac_fn_c_try_link "$LINENO"
|
||||
then :
|
||||
ac_cv_lib_ssh2_libssh2_init=yes
|
||||
else $as_nop
|
||||
ac_cv_lib_ssh2_libssh2_init=no
|
||||
fi
|
||||
rm -f core conftest.err conftest.$ac_objext conftest.beam \
|
||||
conftest$ac_exeext conftest.$ac_ext
|
||||
LIBS=$ac_check_lib_save_LIBS
|
||||
fi
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ssh2_libssh2_init" >&5
|
||||
printf "%s\n" "$ac_cv_lib_ssh2_libssh2_init" >&6; }
|
||||
if test "x$ac_cv_lib_ssh2_libssh2_init" = xyes
|
||||
then :
|
||||
ac_fn_c_check_header_compile "$LINENO" "libssh2.h" "ac_cv_header_libssh2_h" "$ac_includes_default"
|
||||
if test "x$ac_cv_header_libssh2_h" = xyes
|
||||
then :
|
||||
printf "%s\n" "#define HAVE_LIBSSH2 1" >>confdefs.h
|
||||
LIBS="${LIBS} -lssh2"
|
||||
|
||||
else $as_nop
|
||||
as_fn_error $? "header file <libssh2.h> is required" "$LINENO" 5
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libssh2_sftp_init in -lssh2" >&5
|
||||
printf %s "checking for libssh2_sftp_init in -lssh2... " >&6; }
|
||||
if test ${ac_cv_lib_ssh2_libssh2_sftp_init+y}
|
||||
then :
|
||||
printf %s "(cached) " >&6
|
||||
else $as_nop
|
||||
ac_check_lib_save_LIBS=$LIBS
|
||||
LIBS="-lssh2 $LIBS"
|
||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
|
||||
/* Override any GCC internal prototype to avoid an error.
|
||||
Use char because int might match the return type of a GCC
|
||||
builtin and then its argument prototype would still apply. */
|
||||
char libssh2_sftp_init ();
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
return libssh2_sftp_init ();
|
||||
;
|
||||
return 0;
|
||||
}
|
||||
_ACEOF
|
||||
if ac_fn_c_try_link "$LINENO"
|
||||
then :
|
||||
ac_cv_lib_ssh2_libssh2_sftp_init=yes
|
||||
else $as_nop
|
||||
ac_cv_lib_ssh2_libssh2_sftp_init=no
|
||||
fi
|
||||
rm -f core conftest.err conftest.$ac_objext conftest.beam \
|
||||
conftest$ac_exeext conftest.$ac_ext
|
||||
LIBS=$ac_check_lib_save_LIBS
|
||||
fi
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ssh2_libssh2_sftp_init" >&5
|
||||
printf "%s\n" "$ac_cv_lib_ssh2_libssh2_sftp_init" >&6; }
|
||||
if test "x$ac_cv_lib_ssh2_libssh2_sftp_init" = xyes
|
||||
then :
|
||||
ac_fn_c_check_header_compile "$LINENO" "libssh2_sftp.h" "ac_cv_header_libssh2_sftp_h" "$ac_includes_default"
|
||||
if test "x$ac_cv_header_libssh2_sftp_h" = xyes
|
||||
then :
|
||||
|
||||
else $as_nop
|
||||
as_fn_error $? "header file <libssh2_sftp.h> is required" "$LINENO" 5
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
|
||||
# Check optional zst library. Ignore any versions below 1.0.
|
||||
# ----------------------------------------------------------------------------------------------------------------------------------
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for ZSTD_isError in -lzstd" >&5
|
||||
@ -5599,4 +5698,4 @@ if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
|
||||
printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
|
||||
fi
|
||||
|
||||
# Generated from src/build/configure.ac sha1 5bd14429291b37c7b69a19b6086863c15530e138
|
||||
# Generated from src/build/configure.ac sha1 60bcd5099f8e4bfc1e900636b46fc25312c9729f
|
||||
|
@ -47,6 +47,7 @@ Main
|
||||
#include "storage/gcs/helper.h"
|
||||
#include "storage/helper.h"
|
||||
#include "storage/s3/helper.h"
|
||||
#include "storage/sftp/helper.h"
|
||||
#include "version.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
@ -68,6 +69,9 @@ main(int argListSize, const char *argList[])
|
||||
STORAGE_CIFS_HELPER,
|
||||
STORAGE_GCS_HELPER,
|
||||
STORAGE_S3_HELPER,
|
||||
#ifdef HAVE_LIBSSH2
|
||||
STORAGE_SFTP_HELPER,
|
||||
#endif
|
||||
STORAGE_END_HELPER
|
||||
};
|
||||
|
||||
|
@ -253,6 +253,10 @@ src_pgbackrest = [
|
||||
'storage/s3/read.c',
|
||||
'storage/s3/storage.c',
|
||||
'storage/s3/write.c',
|
||||
'storage/sftp/helper.c',
|
||||
'storage/sftp/read.c',
|
||||
'storage/sftp/storage.c',
|
||||
'storage/sftp/write.c',
|
||||
'main.c',
|
||||
]
|
||||
|
||||
@ -268,6 +272,7 @@ executable(
|
||||
lib_openssl,
|
||||
lib_lz4,
|
||||
lib_pq,
|
||||
lib_ssh2,
|
||||
lib_xml,
|
||||
lib_z,
|
||||
lib_zstd,
|
||||
|
38
src/storage/sftp/helper.c
Normal file
38
src/storage/sftp/helper.c
Normal file
@ -0,0 +1,38 @@
|
||||
/***********************************************************************************************************************************
|
||||
SFTP Storage Helper
|
||||
***********************************************************************************************************************************/
|
||||
#include "build.auto.h"
|
||||
|
||||
#ifdef HAVE_LIBSSH2
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/log.h"
|
||||
#include "config/config.h"
|
||||
#include "storage/sftp/helper.h"
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
FN_EXTERN Storage *
|
||||
storageSftpHelper(const unsigned int repoIdx, const bool write, StoragePathExpressionCallback pathExpressionCallback)
|
||||
{
|
||||
FUNCTION_LOG_BEGIN(logLevelDebug);
|
||||
FUNCTION_LOG_PARAM(UINT, repoIdx);
|
||||
FUNCTION_LOG_PARAM(BOOL, write);
|
||||
FUNCTION_LOG_PARAM_P(VOID, pathExpressionCallback);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
ASSERT(cfgOptionIdxStrId(cfgOptRepoType, repoIdx) == STORAGE_SFTP_TYPE);
|
||||
|
||||
FUNCTION_LOG_RETURN(
|
||||
STORAGE,
|
||||
storageSftpNewP(
|
||||
cfgOptionIdxStr(cfgOptRepoPath, repoIdx), cfgOptionIdxStr(cfgOptRepoSftpHost, repoIdx),
|
||||
cfgOptionIdxUInt(cfgOptRepoSftpHostPort, repoIdx), cfgOptionIdxStr(cfgOptRepoSftpHostUser, repoIdx),
|
||||
cfgOptionUInt64(cfgOptIoTimeout), cfgOptionIdxStr(cfgOptRepoSftpPrivateKeyFile, repoIdx),
|
||||
cfgOptionIdxStrId(cfgOptRepoSftpHostKeyHashType, repoIdx), .write = write,
|
||||
.pathExpressionFunction = pathExpressionCallback, .modeFile = STORAGE_MODE_FILE_DEFAULT,
|
||||
.modePath = STORAGE_MODE_PATH_DEFAULT, .keyPub = cfgOptionIdxStrNull(cfgOptRepoSftpPublicKeyFile, repoIdx),
|
||||
.keyPassphrase = cfgOptionIdxStrNull(cfgOptRepoSftpPrivateKeyPassphrase, repoIdx),
|
||||
.hostFingerprint = cfgOptionIdxStrNull(cfgOptRepoSftpHostFingerprint, repoIdx)));
|
||||
}
|
||||
|
||||
#endif // HAVE_LIBSSH2
|
23
src/storage/sftp/helper.h
Normal file
23
src/storage/sftp/helper.h
Normal file
@ -0,0 +1,23 @@
|
||||
/***********************************************************************************************************************************
|
||||
SFTP Storage Helper
|
||||
***********************************************************************************************************************************/
|
||||
#ifndef STORAGE_SFTP_STORAGE_HELPER_H
|
||||
#define STORAGE_SFTP_STORAGE_HELPER_H
|
||||
|
||||
#ifdef HAVE_LIBSSH2
|
||||
|
||||
#include "storage/sftp/storage.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Functions
|
||||
***********************************************************************************************************************************/
|
||||
FN_EXTERN Storage *storageSftpHelper(unsigned int repoIdx, bool write, StoragePathExpressionCallback pathExpressionCallback);
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Storage helper for StorageHelper array passed to storageHelperInit()
|
||||
***********************************************************************************************************************************/
|
||||
#define STORAGE_SFTP_HELPER {.type = STORAGE_SFTP_TYPE, .helper = storageSftpHelper}
|
||||
|
||||
#endif // HAVE_LIBSSH2
|
||||
|
||||
#endif
|
300
src/storage/sftp/read.c
Normal file
300
src/storage/sftp/read.c
Normal file
@ -0,0 +1,300 @@
|
||||
/***********************************************************************************************************************************
|
||||
SFTP Storage Read
|
||||
***********************************************************************************************************************************/
|
||||
#include "build.auto.h"
|
||||
|
||||
#ifdef HAVE_LIBSSH2
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/io/session.h"
|
||||
#include "common/log.h"
|
||||
#include "common/wait.h"
|
||||
#include "storage/read.intern.h"
|
||||
#include "storage/sftp/read.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Object types
|
||||
***********************************************************************************************************************************/
|
||||
typedef struct StorageReadSftp
|
||||
{
|
||||
StorageReadInterface interface; // Interface
|
||||
StorageSftp *storage; // Storage that created this object
|
||||
|
||||
IoSession *ioSession; // IoSession (socket) connection to SFTP server
|
||||
LIBSSH2_SESSION *session; // LibSsh2 session
|
||||
LIBSSH2_SFTP *sftpSession; // LibSsh2 session sftp session
|
||||
LIBSSH2_SFTP_HANDLE *sftpHandle; // LibSsh2 session sftp handle
|
||||
LIBSSH2_SFTP_ATTRIBUTES *attr; // LibSsh2 file attributes
|
||||
uint64_t current; // Current bytes read from file
|
||||
uint64_t limit; // Limit bytes to be read from file (UINT64_MAX for no limit)
|
||||
bool eof; // Did we reach end of file
|
||||
TimeMSec timeout; // Session timeout
|
||||
} StorageReadSftp;
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Macros for function logging
|
||||
***********************************************************************************************************************************/
|
||||
#define FUNCTION_LOG_STORAGE_READ_SFTP_TYPE \
|
||||
StorageReadSftp *
|
||||
#define FUNCTION_LOG_STORAGE_READ_SFTP_FORMAT(value, buffer, bufferSize) \
|
||||
objNameToLog(value, "StorageReadSftp", buffer, bufferSize)
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Open the file
|
||||
***********************************************************************************************************************************/
|
||||
static bool
|
||||
storageReadSftpOpen(THIS_VOID)
|
||||
{
|
||||
THIS(StorageReadSftp);
|
||||
|
||||
FUNCTION_LOG_BEGIN(logLevelTrace);
|
||||
FUNCTION_LOG_PARAM(STORAGE_READ_SFTP, this);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
ASSERT(this != NULL);
|
||||
|
||||
// Open the file
|
||||
Wait *const wait = waitNew(this->timeout);
|
||||
|
||||
do
|
||||
{
|
||||
this->sftpHandle = libssh2_sftp_open_ex(
|
||||
this->sftpSession, strZ(this->interface.name), (unsigned int)strSize(this->interface.name), LIBSSH2_FXF_READ, 0,
|
||||
LIBSSH2_SFTP_OPENFILE);
|
||||
}
|
||||
while (this->sftpHandle == NULL && waitMore(wait));
|
||||
|
||||
waitFree(wait);
|
||||
|
||||
if (this->sftpHandle == NULL)
|
||||
{
|
||||
int rc = libssh2_session_last_errno(this->session);
|
||||
|
||||
if (rc == LIBSSH2_ERROR_SFTP_PROTOCOL || rc == LIBSSH2_ERROR_EAGAIN)
|
||||
{
|
||||
if (libssh2_sftp_last_error(this->sftpSession) == LIBSSH2_FX_NO_SUCH_FILE)
|
||||
{
|
||||
if (!this->interface.ignoreMissing)
|
||||
THROW_FMT(FileMissingError, STORAGE_ERROR_READ_MISSING, strZ(this->interface.name));
|
||||
}
|
||||
else
|
||||
THROW_FMT(FileOpenError, STORAGE_ERROR_READ_OPEN, strZ(this->interface.name));
|
||||
}
|
||||
}
|
||||
// Else success
|
||||
else
|
||||
{
|
||||
// Seek to offset, libssh2_sftp_seek64 returns void
|
||||
if (this->interface.offset != 0)
|
||||
libssh2_sftp_seek64(this->sftpHandle, this->interface.offset);
|
||||
}
|
||||
|
||||
FUNCTION_LOG_RETURN(BOOL, this->sftpHandle != NULL);
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Read from a file
|
||||
***********************************************************************************************************************************/
|
||||
static size_t
|
||||
storageReadSftp(THIS_VOID, Buffer *const buffer, const bool block)
|
||||
{
|
||||
THIS(StorageReadSftp);
|
||||
|
||||
FUNCTION_LOG_BEGIN(logLevelTrace);
|
||||
FUNCTION_LOG_PARAM(STORAGE_READ_SFTP, this);
|
||||
FUNCTION_LOG_PARAM(BUFFER, buffer);
|
||||
FUNCTION_LOG_PARAM(BOOL, block);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
ASSERT(this != NULL && this->sftpHandle != NULL);
|
||||
ASSERT(buffer != NULL && !bufFull(buffer));
|
||||
|
||||
ssize_t actualBytes = 0;
|
||||
|
||||
// Read if EOF has not been reached
|
||||
if (!this->eof)
|
||||
{
|
||||
// Determine expected bytes to read. If remaining size in the buffer would exceed the limit then reduce the expected read.
|
||||
size_t expectedBytes = bufRemains(buffer);
|
||||
|
||||
if (this->current + expectedBytes > this->limit)
|
||||
expectedBytes = (size_t)(this->limit - this->current);
|
||||
|
||||
bufLimitSet(buffer, expectedBytes);
|
||||
ssize_t rc = 0;
|
||||
|
||||
// Read until EOF or buffer is full
|
||||
do
|
||||
{
|
||||
Wait *const wait = waitNew(this->timeout);
|
||||
|
||||
do
|
||||
{
|
||||
rc = libssh2_sftp_read(this->sftpHandle, (char *)bufRemainsPtr(buffer), bufRemains(buffer));
|
||||
}
|
||||
while (rc == LIBSSH2_ERROR_EAGAIN && waitMore(wait));
|
||||
|
||||
waitFree(wait);
|
||||
|
||||
// Break on EOF or error
|
||||
if (rc <= 0)
|
||||
break;
|
||||
|
||||
// Account/shift for bytes read
|
||||
bufUsedInc(buffer, (size_t)rc);
|
||||
}
|
||||
while (!bufFull(buffer));
|
||||
|
||||
// Total bytes read into the buffer
|
||||
actualBytes = (ssize_t)bufUsed(buffer);
|
||||
|
||||
// Error occurred during read
|
||||
if (rc < 0)
|
||||
{
|
||||
if (rc == LIBSSH2_ERROR_SFTP_PROTOCOL)
|
||||
{
|
||||
uint64_t sftpErr = 0;
|
||||
|
||||
// libssh2 sftp lseek seems to return LIBSSH2_FX_BAD_MESSAGE on a seek too far
|
||||
if ((sftpErr = libssh2_sftp_last_error(this->sftpSession)) == LIBSSH2_FX_BAD_MESSAGE && this->interface.offset > 0)
|
||||
THROW_FMT(FileOpenError, STORAGE_ERROR_READ_SEEK, this->interface.offset, strZ(this->interface.name));
|
||||
else
|
||||
THROW_FMT(FileReadError, "unable to read '%s': sftp errno [%" PRIu64 "]", strZ(this->interface.name), sftpErr);
|
||||
}
|
||||
else
|
||||
THROW_FMT(FileReadError, "unable to read '%s'", strZ(this->interface.name));
|
||||
}
|
||||
|
||||
// Update amount of buffer used
|
||||
this->current += (uint64_t)actualBytes;
|
||||
|
||||
// If less data than expected was read or the limit has been reached then EOF. The file may not actually be EOF but we are
|
||||
// not concerned with files that are growing. Just read up to the point where the file is being extended.
|
||||
if ((size_t)actualBytes != expectedBytes || this->current == this->limit)
|
||||
this->eof = true;
|
||||
}
|
||||
|
||||
FUNCTION_LOG_RETURN(SIZE, (size_t)actualBytes);
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Close the file
|
||||
***********************************************************************************************************************************/
|
||||
static void
|
||||
storageReadSftpClose(THIS_VOID)
|
||||
{
|
||||
THIS(StorageReadSftp);
|
||||
|
||||
FUNCTION_LOG_BEGIN(logLevelTrace);
|
||||
FUNCTION_LOG_PARAM(STORAGE_READ_SFTP, this);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
ASSERT(this != NULL);
|
||||
|
||||
if (this->sftpHandle != NULL)
|
||||
{
|
||||
int rc = 0;
|
||||
Wait *const wait = waitNew(this->timeout);
|
||||
|
||||
// Close the file
|
||||
do
|
||||
{
|
||||
rc = libssh2_sftp_close(this->sftpHandle);
|
||||
}
|
||||
while (rc == LIBSSH2_ERROR_EAGAIN && waitMore(wait));
|
||||
|
||||
waitFree(wait);
|
||||
|
||||
if (rc)
|
||||
{
|
||||
THROW_FMT(
|
||||
FileCloseError,
|
||||
STORAGE_ERROR_READ_CLOSE ": libssh2 errno [%d]%s", strZ(this->interface.name), rc,
|
||||
rc == LIBSSH2_ERROR_SFTP_PROTOCOL ?
|
||||
strZ(strNewFmt(": sftp errno [%lu]", libssh2_sftp_last_error(this->sftpSession))) : "");
|
||||
}
|
||||
}
|
||||
|
||||
this->sftpHandle = NULL;
|
||||
|
||||
FUNCTION_LOG_RETURN_VOID();
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Has file reached EOF?
|
||||
***********************************************************************************************************************************/
|
||||
static bool
|
||||
storageReadSftpEof(THIS_VOID)
|
||||
{
|
||||
THIS(StorageReadSftp);
|
||||
|
||||
FUNCTION_TEST_BEGIN();
|
||||
FUNCTION_TEST_PARAM(STORAGE_READ_SFTP, this);
|
||||
FUNCTION_TEST_END();
|
||||
|
||||
ASSERT(this != NULL);
|
||||
|
||||
FUNCTION_TEST_RETURN(BOOL, this->eof);
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
FN_EXTERN StorageRead *
|
||||
storageReadSftpNew(
|
||||
StorageSftp *const storage, const String *const name, const bool ignoreMissing, IoSession *const ioSession,
|
||||
LIBSSH2_SESSION *const session, LIBSSH2_SFTP *const sftpSession, LIBSSH2_SFTP_HANDLE *const sftpHandle,
|
||||
const TimeMSec timeout, const uint64_t offset, const Variant *const limit)
|
||||
{
|
||||
FUNCTION_LOG_BEGIN(logLevelTrace);
|
||||
FUNCTION_LOG_PARAM(STRING, name);
|
||||
FUNCTION_LOG_PARAM(BOOL, ignoreMissing);
|
||||
FUNCTION_LOG_PARAM(IO_SESSION, ioSession);
|
||||
FUNCTION_LOG_PARAM_P(VOID, session);
|
||||
FUNCTION_LOG_PARAM_P(VOID, sftpSession);
|
||||
FUNCTION_LOG_PARAM_P(VOID, sftpHandle);
|
||||
FUNCTION_LOG_PARAM(TIME_MSEC, timeout);
|
||||
FUNCTION_LOG_PARAM(UINT64, offset);
|
||||
FUNCTION_LOG_PARAM(VARIANT, limit);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
ASSERT(name != NULL);
|
||||
|
||||
OBJ_NEW_BEGIN(StorageReadSftp, .childQty = MEM_CONTEXT_QTY_MAX)
|
||||
{
|
||||
*this = (StorageReadSftp)
|
||||
{
|
||||
.storage = storage,
|
||||
.ioSession = ioSession,
|
||||
.session = session,
|
||||
.sftpSession = sftpSession,
|
||||
.sftpHandle = sftpHandle,
|
||||
.timeout = timeout,
|
||||
|
||||
// Rather than enable/disable limit checking just use a big number when there is no limit. We can feel pretty confident
|
||||
// that no files will be > UINT64_MAX in size. This is a copy of the interface limit but it simplifies the code during
|
||||
// read so it seems worthwhile.
|
||||
.limit = limit == NULL ? UINT64_MAX : varUInt64(limit),
|
||||
|
||||
.interface = (StorageReadInterface)
|
||||
{
|
||||
.type = STORAGE_SFTP_TYPE,
|
||||
.name = strDup(name),
|
||||
.ignoreMissing = ignoreMissing,
|
||||
.offset = offset,
|
||||
.limit = varDup(limit),
|
||||
|
||||
.ioInterface = (IoReadInterface)
|
||||
{
|
||||
.close = storageReadSftpClose,
|
||||
.eof = storageReadSftpEof,
|
||||
.open = storageReadSftpOpen,
|
||||
.read = storageReadSftp,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
OBJ_NEW_END();
|
||||
|
||||
FUNCTION_LOG_RETURN(STORAGE_READ, storageReadNew(this, &this->interface));
|
||||
}
|
||||
|
||||
#endif // HAVE_LIBSSH2
|
17
src/storage/sftp/read.h
Normal file
17
src/storage/sftp/read.h
Normal file
@ -0,0 +1,17 @@
|
||||
/***********************************************************************************************************************************
|
||||
SFTP Storage Read
|
||||
***********************************************************************************************************************************/
|
||||
#ifndef STORAGE_SFTP_READ_H
|
||||
#define STORAGE_SFTP_READ_H
|
||||
|
||||
#include "storage/read.h"
|
||||
#include "storage/sftp/storage.intern.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Constructors
|
||||
***********************************************************************************************************************************/
|
||||
FN_EXTERN StorageRead *storageReadSftpNew(
|
||||
StorageSftp *storage, const String *name, bool ignoreMissing, IoSession *ioSession, LIBSSH2_SESSION *session,
|
||||
LIBSSH2_SFTP *sftpSession, LIBSSH2_SFTP_HANDLE *sftpHandle, TimeMSec timeout, uint64_t offset, const Variant *limit);
|
||||
|
||||
#endif
|
891
src/storage/sftp/storage.c
Normal file
891
src/storage/sftp/storage.c
Normal file
@ -0,0 +1,891 @@
|
||||
/***********************************************************************************************************************************
|
||||
SFTP Storage
|
||||
***********************************************************************************************************************************/
|
||||
#include "build.auto.h"
|
||||
|
||||
#ifdef HAVE_LIBSSH2
|
||||
|
||||
#include "common/crypto/hash.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/io/socket/client.h"
|
||||
#include "common/log.h"
|
||||
#include "common/user.h"
|
||||
#include "common/wait.h"
|
||||
#include "storage/sftp/read.h"
|
||||
#include "storage/sftp/storage.intern.h"
|
||||
#include "storage/sftp/write.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Define PATH_MAX if it is not defined
|
||||
***********************************************************************************************************************************/
|
||||
#ifndef PATH_MAX
|
||||
#define PATH_MAX (4 * 1024)
|
||||
#endif
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Object type
|
||||
***********************************************************************************************************************************/
|
||||
struct StorageSftp
|
||||
{
|
||||
STORAGE_COMMON_MEMBER;
|
||||
|
||||
IoSession *ioSession; // IoSession (socket) connection to SFTP server
|
||||
LIBSSH2_SESSION *session; // LibSsh2 session
|
||||
LIBSSH2_SFTP *sftpSession; // LibSsh2 session sftp session
|
||||
LIBSSH2_SFTP_HANDLE *sftpHandle; // LibSsh2 session sftp handle
|
||||
TimeMSec timeout; // Session timeout
|
||||
};
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Free libssh2 resources
|
||||
***********************************************************************************************************************************/
|
||||
static void
|
||||
storageSftpLibSsh2SessionFreeResource(THIS_VOID)
|
||||
{
|
||||
THIS(StorageSftp);
|
||||
|
||||
FUNCTION_LOG_BEGIN(logLevelTrace);
|
||||
FUNCTION_LOG_PARAM(STORAGE_SFTP, this);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
ASSERT(this != NULL);
|
||||
|
||||
int rc;
|
||||
|
||||
if (this->sftpHandle != NULL)
|
||||
{
|
||||
do
|
||||
{
|
||||
rc = libssh2_sftp_close(this->sftpHandle);
|
||||
}
|
||||
while (rc == LIBSSH2_ERROR_EAGAIN);
|
||||
|
||||
if (rc != 0)
|
||||
{
|
||||
THROW_FMT(
|
||||
ServiceError, "failed to free resource sftpHandle: libssh2 errno [%d]%s", rc,
|
||||
rc == LIBSSH2_ERROR_SFTP_PROTOCOL ?
|
||||
strZ(strNewFmt(": sftp errno [%lu]", libssh2_sftp_last_error(this->sftpSession))) : "");
|
||||
}
|
||||
}
|
||||
|
||||
if (this->sftpSession != NULL)
|
||||
{
|
||||
do
|
||||
{
|
||||
rc = libssh2_sftp_shutdown(this->sftpSession);
|
||||
}
|
||||
while (rc == LIBSSH2_ERROR_EAGAIN);
|
||||
|
||||
if (rc != 0)
|
||||
{
|
||||
THROW_FMT(
|
||||
ServiceError, "failed to free resource sftpSession: libssh2 errno [%d]%s", rc,
|
||||
rc == LIBSSH2_ERROR_SFTP_PROTOCOL ?
|
||||
strZ(strNewFmt(": sftp errno [%lu]", libssh2_sftp_last_error(this->sftpSession))) : "");
|
||||
}
|
||||
}
|
||||
|
||||
if (this->session != NULL)
|
||||
{
|
||||
do
|
||||
{
|
||||
rc = libssh2_session_disconnect_ex(this->session, SSH_DISCONNECT_BY_APPLICATION, "pgbackrest instance shutdown", "");
|
||||
}
|
||||
while (rc == LIBSSH2_ERROR_EAGAIN);
|
||||
|
||||
if (rc != 0)
|
||||
THROW_FMT(ServiceError, "failed to disconnect libssh2 session: libssh2 errno [%d]", rc);
|
||||
|
||||
do
|
||||
{
|
||||
rc = libssh2_session_free(this->session);
|
||||
}
|
||||
while (rc == LIBSSH2_ERROR_EAGAIN);
|
||||
|
||||
if (rc != 0)
|
||||
THROW_FMT(ServiceError, "failed to free libssh2 session: libssh2 errno [%d]", rc);
|
||||
}
|
||||
|
||||
libssh2_exit();
|
||||
|
||||
FUNCTION_LOG_RETURN_VOID();
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
FN_EXTERN FN_NO_RETURN void
|
||||
storageSftpEvalLibSsh2Error(
|
||||
const int ssh2Errno, const uint64_t sftpErrno, const ErrorType *const errorType, const String *const message,
|
||||
const String *const hint)
|
||||
{
|
||||
FUNCTION_TEST_BEGIN();
|
||||
FUNCTION_TEST_PARAM(INT, ssh2Errno);
|
||||
FUNCTION_TEST_PARAM(UINT64, sftpErrno);
|
||||
FUNCTION_TEST_PARAM(ERROR_TYPE, errorType);
|
||||
FUNCTION_TEST_PARAM(STRING, message);
|
||||
FUNCTION_TEST_PARAM(STRING, hint);
|
||||
FUNCTION_TEST_END();
|
||||
|
||||
ASSERT(errorType != NULL);
|
||||
|
||||
THROWP_FMT(
|
||||
errorType, "%slibssh2 error [%d]%s%s", message != NULL ? zNewFmt("%s: ", strZ(message)) : "", ssh2Errno,
|
||||
ssh2Errno == LIBSSH2_ERROR_SFTP_PROTOCOL ? zNewFmt(": sftp error [%" PRIu64 "]", sftpErrno) : "",
|
||||
hint != NULL ? zNewFmt("\n%s", strZ(hint)) : "");
|
||||
|
||||
FUNCTION_TEST_NO_RETURN();
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
static bool
|
||||
storageSftpLibSsh2FxNoSuchFile(THIS_VOID, const int rc)
|
||||
{
|
||||
THIS(StorageSftp);
|
||||
|
||||
FUNCTION_TEST_BEGIN();
|
||||
FUNCTION_TEST_PARAM(STORAGE_SFTP, this);
|
||||
FUNCTION_TEST_PARAM(INT, rc);
|
||||
FUNCTION_TEST_END();
|
||||
|
||||
ASSERT(this != NULL);
|
||||
|
||||
FUNCTION_TEST_RETURN(
|
||||
BOOL, rc == LIBSSH2_ERROR_SFTP_PROTOCOL && libssh2_sftp_last_error(this->sftpSession) == LIBSSH2_FX_NO_SUCH_FILE);
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
static StorageInfo
|
||||
storageSftpInfo(THIS_VOID, const String *const file, const StorageInfoLevel level, const StorageInterfaceInfoParam param)
|
||||
{
|
||||
THIS(StorageSftp);
|
||||
|
||||
FUNCTION_LOG_BEGIN(logLevelTrace);
|
||||
FUNCTION_LOG_PARAM(STORAGE_SFTP, this);
|
||||
FUNCTION_LOG_PARAM(STRING, file);
|
||||
FUNCTION_LOG_PARAM(ENUM, level);
|
||||
FUNCTION_LOG_PARAM(BOOL, param.followLink);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
FUNCTION_AUDIT_STRUCT();
|
||||
|
||||
ASSERT(this != NULL);
|
||||
ASSERT(file != NULL);
|
||||
|
||||
StorageInfo result = {.level = level};
|
||||
|
||||
// Stat the file to check if it exists
|
||||
LIBSSH2_SFTP_ATTRIBUTES attr;
|
||||
int rc;
|
||||
Wait *const wait = waitNew(this->timeout);
|
||||
|
||||
do
|
||||
{
|
||||
rc = libssh2_sftp_stat_ex(
|
||||
this->sftpSession, strZ(file), (unsigned int)strSize(file), param.followLink ? LIBSSH2_SFTP_STAT : LIBSSH2_SFTP_LSTAT,
|
||||
&attr);
|
||||
}
|
||||
while (rc == LIBSSH2_ERROR_EAGAIN && waitMore(wait));
|
||||
|
||||
waitFree(wait);
|
||||
|
||||
// Throw libssh2 errors other than no such file
|
||||
if (rc != 0)
|
||||
{
|
||||
if (!storageSftpLibSsh2FxNoSuchFile(this, rc))
|
||||
THROW_FMT(FileOpenError, STORAGE_ERROR_INFO, strZ(file));
|
||||
}
|
||||
// Else the file exists
|
||||
else
|
||||
{
|
||||
result.exists = true;
|
||||
|
||||
// Add type info (no need set file type since it is the default)
|
||||
if (result.level >= storageInfoLevelType && !LIBSSH2_SFTP_S_ISREG(attr.permissions))
|
||||
{
|
||||
if (LIBSSH2_SFTP_S_ISDIR(attr.permissions))
|
||||
result.type = storageTypePath;
|
||||
else if (LIBSSH2_SFTP_S_ISLNK(attr.permissions))
|
||||
result.type = storageTypeLink;
|
||||
else
|
||||
result.type = storageTypeSpecial;
|
||||
}
|
||||
|
||||
// Add basic level info
|
||||
if (result.level >= storageInfoLevelBasic)
|
||||
{
|
||||
if ((attr.flags & LIBSSH2_SFTP_ATTR_ACMODTIME) != 0)
|
||||
result.timeModified = (time_t)attr.mtime;
|
||||
|
||||
if (result.type == storageTypeFile)
|
||||
if ((attr.flags & LIBSSH2_SFTP_ATTR_SIZE) != 0)
|
||||
result.size = (uint64_t)attr.filesize;
|
||||
}
|
||||
|
||||
// Add detail level info
|
||||
if (result.level >= storageInfoLevelDetail)
|
||||
{
|
||||
if ((attr.flags & LIBSSH2_SFTP_ATTR_UIDGID) != 0)
|
||||
{
|
||||
result.groupId = (unsigned int)attr.gid;
|
||||
result.userId = (unsigned int)attr.uid;
|
||||
}
|
||||
|
||||
if ((attr.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) != 0)
|
||||
result.mode = attr.permissions & (LIBSSH2_SFTP_S_IRWXU | LIBSSH2_SFTP_S_IRWXG | LIBSSH2_SFTP_S_IRWXO);
|
||||
|
||||
if (result.type == storageTypeLink)
|
||||
{
|
||||
char linkDestination[PATH_MAX] = {0};
|
||||
ssize_t linkDestinationSize = 0;
|
||||
|
||||
Wait *const wait = waitNew(this->timeout);
|
||||
|
||||
do
|
||||
{
|
||||
linkDestinationSize = libssh2_sftp_symlink_ex(
|
||||
this->sftpSession, strZ(file), (unsigned int)strSize(file), linkDestination, PATH_MAX - 1,
|
||||
LIBSSH2_SFTP_READLINK);
|
||||
}
|
||||
while (linkDestinationSize == LIBSSH2_ERROR_EAGAIN && waitMore(wait));
|
||||
|
||||
waitFree(wait);
|
||||
|
||||
if (linkDestinationSize < 0)
|
||||
THROW_FMT(FileReadError, "unable to get destination for link '%s'", strZ(file));
|
||||
|
||||
result.linkDestination = strNewZN(linkDestination, (size_t)linkDestinationSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FUNCTION_LOG_RETURN(STORAGE_INFO, result);
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
// Helper function to get info for a file if it exists. This logic can't live directly in storageSftpList() because there is a race
|
||||
// condition where a file might exist while listing the directory but it is gone before stat() can be called. In order to get
|
||||
// complete test coverage this function must be split out.
|
||||
static void
|
||||
storageSftpListEntry(
|
||||
StorageSftp *const this, StorageList *const list, const String *const path, const char *const name,
|
||||
const StorageInfoLevel level)
|
||||
{
|
||||
FUNCTION_TEST_BEGIN();
|
||||
FUNCTION_TEST_PARAM(STORAGE_SFTP, this);
|
||||
FUNCTION_TEST_PARAM(STORAGE_LIST, list);
|
||||
FUNCTION_TEST_PARAM(STRING, path);
|
||||
FUNCTION_TEST_PARAM(STRINGZ, name);
|
||||
FUNCTION_TEST_PARAM(ENUM, level);
|
||||
FUNCTION_TEST_END();
|
||||
|
||||
FUNCTION_AUDIT_HELPER();
|
||||
|
||||
ASSERT(this != NULL);
|
||||
ASSERT(list != NULL);
|
||||
ASSERT(path != NULL);
|
||||
ASSERT(name != NULL);
|
||||
|
||||
StorageInfo info = storageInterfaceInfoP(this, strNewFmt("%s/%s", strZ(path), name), level);
|
||||
|
||||
if (info.exists)
|
||||
{
|
||||
info.name = STR(name);
|
||||
storageLstAdd(list, &info);
|
||||
}
|
||||
|
||||
FUNCTION_TEST_RETURN_VOID();
|
||||
}
|
||||
|
||||
static StorageList *
|
||||
storageSftpList(THIS_VOID, const String *const path, const StorageInfoLevel level, const StorageInterfaceListParam param)
|
||||
{
|
||||
THIS(StorageSftp);
|
||||
|
||||
FUNCTION_LOG_BEGIN(logLevelTrace);
|
||||
FUNCTION_LOG_PARAM(STORAGE_SFTP, this);
|
||||
FUNCTION_LOG_PARAM(STRING, path);
|
||||
FUNCTION_LOG_PARAM(ENUM, level);
|
||||
(void)param; // No parameters are used
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
ASSERT(this != NULL);
|
||||
ASSERT(path != NULL);
|
||||
|
||||
StorageList *result = NULL;
|
||||
|
||||
// Open the directory for read
|
||||
LIBSSH2_SFTP_HANDLE *sftpHandle;
|
||||
Wait *const wait = waitNew(this->timeout);
|
||||
|
||||
do
|
||||
{
|
||||
sftpHandle = libssh2_sftp_open_ex(this->sftpSession, strZ(path), (unsigned int)strSize(path), 0, 0, LIBSSH2_SFTP_OPENDIR);
|
||||
}
|
||||
while (sftpHandle == NULL && libssh2_session_last_errno(this->session) == LIBSSH2_ERROR_EAGAIN && waitMore(wait));
|
||||
|
||||
waitFree(wait);
|
||||
|
||||
// If the directory could not be opened process errors and report missing directories
|
||||
if (sftpHandle == NULL)
|
||||
{
|
||||
const int rc = libssh2_session_last_errno(this->session);
|
||||
|
||||
// If sftpHandle == NULL is due to LIBSSH2_FX_NO_SUCH_FILE, do not throw error here, return NULL result
|
||||
if (!storageSftpLibSsh2FxNoSuchFile(this, rc))
|
||||
{
|
||||
storageSftpEvalLibSsh2Error(
|
||||
rc, libssh2_sftp_last_error(this->sftpSession), &PathOpenError, strNewFmt(STORAGE_ERROR_LIST_INFO, strZ(path)),
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Directory was found
|
||||
result = storageLstNew(level);
|
||||
|
||||
TRY_BEGIN()
|
||||
{
|
||||
MEM_CONTEXT_TEMP_RESET_BEGIN()
|
||||
{
|
||||
LIBSSH2_SFTP_ATTRIBUTES attr;
|
||||
char filename[PATH_MAX] = {0};
|
||||
int len;
|
||||
|
||||
Wait *wait = waitNew(this->timeout);
|
||||
|
||||
// Read the directory entries
|
||||
do
|
||||
{
|
||||
len = libssh2_sftp_readdir_ex(sftpHandle, filename, PATH_MAX - 1, NULL, 0, &attr);
|
||||
|
||||
if (len > 0)
|
||||
{
|
||||
filename[len] = '\0';
|
||||
|
||||
// Always skip . and ..
|
||||
if (!strEqZ(DOT_STR, filename) && !strEqZ(DOTDOT_STR, filename))
|
||||
{
|
||||
if (level == storageInfoLevelExists)
|
||||
{
|
||||
const StorageInfo storageInfo =
|
||||
{
|
||||
.name = STR(filename),
|
||||
.level = storageInfoLevelExists,
|
||||
.exists = true,
|
||||
};
|
||||
|
||||
storageLstAdd(result, &storageInfo);
|
||||
}
|
||||
else
|
||||
storageSftpListEntry(this, result, path, filename, level);
|
||||
}
|
||||
|
||||
// Reset the memory context occasionally so we don't use too much memory or slow down processing
|
||||
MEM_CONTEXT_TEMP_RESET(1000);
|
||||
|
||||
// Reset the timeout so we don't timeout before reading all entries
|
||||
waitFree(wait);
|
||||
wait = waitNew(this->timeout);
|
||||
}
|
||||
}
|
||||
while (len > 0 || (len == LIBSSH2_ERROR_EAGAIN && waitMore(wait)));
|
||||
|
||||
waitFree(wait);
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
}
|
||||
FINALLY()
|
||||
{
|
||||
int rc;
|
||||
Wait *const wait = waitNew(this->timeout);
|
||||
|
||||
do
|
||||
{
|
||||
rc = libssh2_sftp_closedir(sftpHandle);
|
||||
}
|
||||
while (rc == LIBSSH2_ERROR_EAGAIN && waitMore(wait));
|
||||
|
||||
waitFree(wait);
|
||||
|
||||
if (rc != 0)
|
||||
THROW_FMT(PathCloseError, "unable to close path '%s' after listing", strZ(path));
|
||||
|
||||
sftpHandle = NULL;
|
||||
}
|
||||
TRY_END();
|
||||
}
|
||||
|
||||
FUNCTION_LOG_RETURN(STORAGE_LIST, result);
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
static void
|
||||
storageSftpRemove(THIS_VOID, const String *const file, const StorageInterfaceRemoveParam param)
|
||||
{
|
||||
THIS(StorageSftp);
|
||||
|
||||
FUNCTION_LOG_BEGIN(logLevelTrace);
|
||||
FUNCTION_LOG_PARAM(STORAGE_SFTP, this);
|
||||
FUNCTION_LOG_PARAM(STRING, file);
|
||||
FUNCTION_LOG_PARAM(BOOL, param.errorOnMissing);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
ASSERT(this != NULL);
|
||||
ASSERT(file != NULL);
|
||||
|
||||
// Attempt to unlink the file
|
||||
int rc;
|
||||
Wait *const wait = waitNew(this->timeout);
|
||||
|
||||
do
|
||||
{
|
||||
rc = libssh2_sftp_unlink_ex(this->sftpSession, strZ(file), (unsigned int)strSize(file));
|
||||
}
|
||||
while (rc == LIBSSH2_ERROR_EAGAIN && waitMore(wait));
|
||||
|
||||
if (rc != 0)
|
||||
{
|
||||
if (rc == LIBSSH2_ERROR_SFTP_PROTOCOL)
|
||||
{
|
||||
if (param.errorOnMissing || !storageSftpLibSsh2FxNoSuchFile(this, rc))
|
||||
{
|
||||
storageSftpEvalLibSsh2Error(
|
||||
rc, libssh2_sftp_last_error(this->sftpSession), &FileRemoveError,
|
||||
strNewFmt("unable to remove '%s'", strZ(file)), NULL);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (param.errorOnMissing)
|
||||
THROW_FMT(FileRemoveError, "unable to remove '%s'", strZ(file));
|
||||
}
|
||||
}
|
||||
|
||||
FUNCTION_LOG_RETURN_VOID();
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
static StorageRead *
|
||||
storageSftpNewRead(THIS_VOID, const String *const file, const bool ignoreMissing, const StorageInterfaceNewReadParam param)
|
||||
{
|
||||
THIS(StorageSftp);
|
||||
|
||||
FUNCTION_LOG_BEGIN(logLevelTrace);
|
||||
FUNCTION_LOG_PARAM(STORAGE_SFTP, this);
|
||||
FUNCTION_LOG_PARAM(STRING, file);
|
||||
FUNCTION_LOG_PARAM(BOOL, ignoreMissing);
|
||||
FUNCTION_LOG_PARAM(UINT64, param.offset);
|
||||
FUNCTION_LOG_PARAM(VARIANT, param.limit);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
ASSERT(this != NULL);
|
||||
ASSERT(file != NULL);
|
||||
|
||||
FUNCTION_LOG_RETURN(
|
||||
STORAGE_READ,
|
||||
storageReadSftpNew(
|
||||
this, file, ignoreMissing, this->ioSession, this->session, this->sftpSession, this->sftpHandle, this->timeout,
|
||||
param.offset, param.limit));
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
static StorageWrite *
|
||||
storageSftpNewWrite(THIS_VOID, const String *const file, const StorageInterfaceNewWriteParam param)
|
||||
{
|
||||
THIS(StorageSftp);
|
||||
|
||||
FUNCTION_LOG_BEGIN(logLevelTrace);
|
||||
FUNCTION_LOG_PARAM(STORAGE_SFTP, this);
|
||||
FUNCTION_LOG_PARAM(STRING, file);
|
||||
FUNCTION_LOG_PARAM(MODE, param.modeFile);
|
||||
FUNCTION_LOG_PARAM(MODE, param.modePath);
|
||||
FUNCTION_LOG_PARAM(STRING, param.user);
|
||||
FUNCTION_LOG_PARAM(STRING, param.group);
|
||||
FUNCTION_LOG_PARAM(TIME, param.timeModified);
|
||||
FUNCTION_LOG_PARAM(BOOL, param.createPath);
|
||||
FUNCTION_LOG_PARAM(BOOL, param.syncFile);
|
||||
FUNCTION_LOG_PARAM(BOOL, param.syncPath);
|
||||
FUNCTION_LOG_PARAM(BOOL, param.atomic);
|
||||
FUNCTION_LOG_PARAM(BOOL, param.truncate);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
ASSERT(this != NULL);
|
||||
ASSERT(file != NULL);
|
||||
ASSERT(param.createPath);
|
||||
ASSERT(param.truncate);
|
||||
ASSERT(param.user == NULL);
|
||||
ASSERT(param.group == NULL);
|
||||
ASSERT(param.timeModified == 0);
|
||||
|
||||
FUNCTION_LOG_RETURN(
|
||||
STORAGE_WRITE,
|
||||
storageWriteSftpNew(
|
||||
this, file, this->ioSession, this->session, this->sftpSession, this->sftpHandle, this->timeout, param.modeFile,
|
||||
param.modePath, param.user, param.group, param.timeModified, param.createPath, param.syncFile,
|
||||
this->interface.pathSync != NULL ? param.syncPath : false, param.atomic, param.truncate));
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
static void
|
||||
storageSftpPathCreate(
|
||||
THIS_VOID, const String *const path, const bool errorOnExists, const bool noParentCreate, const mode_t mode,
|
||||
const StorageInterfacePathCreateParam param)
|
||||
{
|
||||
THIS(StorageSftp);
|
||||
|
||||
FUNCTION_LOG_BEGIN(logLevelTrace);
|
||||
FUNCTION_LOG_PARAM(STORAGE_SFTP, this);
|
||||
FUNCTION_LOG_PARAM(STRING, path);
|
||||
FUNCTION_LOG_PARAM(BOOL, errorOnExists);
|
||||
FUNCTION_LOG_PARAM(BOOL, noParentCreate);
|
||||
FUNCTION_LOG_PARAM(MODE, mode);
|
||||
(void)param; // No parameters are used
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
ASSERT(this != NULL);
|
||||
ASSERT(path != NULL);
|
||||
|
||||
int rc;
|
||||
Wait *const wait = waitNew(this->timeout);
|
||||
|
||||
// Attempt to create the directory
|
||||
do
|
||||
{
|
||||
rc = libssh2_sftp_mkdir_ex(this->sftpSession, strZ(path), (unsigned int)strSize(path), (int)mode);
|
||||
}
|
||||
while (rc == LIBSSH2_ERROR_EAGAIN && waitMore(wait));
|
||||
|
||||
waitFree(wait);
|
||||
|
||||
if (rc != 0)
|
||||
{
|
||||
if (rc == LIBSSH2_ERROR_SFTP_PROTOCOL)
|
||||
{
|
||||
uint64_t sftpErrno = libssh2_sftp_last_error(this->sftpSession);
|
||||
|
||||
// libssh2 may return LIBSSH2_FX_FAILURE if the directory already exists
|
||||
if (sftpErrno == LIBSSH2_FX_FAILURE)
|
||||
{
|
||||
// Check if the directory already exists
|
||||
LIBSSH2_SFTP_ATTRIBUTES attr;
|
||||
|
||||
Wait *const wait = waitNew(this->timeout);
|
||||
|
||||
do
|
||||
{
|
||||
rc = libssh2_sftp_stat_ex(
|
||||
this->sftpSession, strZ(path), (unsigned int)strSize(path), LIBSSH2_SFTP_STAT, &attr);
|
||||
}
|
||||
while (rc == LIBSSH2_ERROR_EAGAIN && waitMore(wait));
|
||||
|
||||
waitFree(wait);
|
||||
|
||||
// If rc = 0 then already exists
|
||||
if (rc == 0 && errorOnExists)
|
||||
THROW_FMT(PathCreateError, "unable to create path '%s': path already exists", strZ(path));
|
||||
}
|
||||
// If the parent path does not exist then create it if allowed
|
||||
else if (sftpErrno == LIBSSH2_FX_NO_SUCH_FILE && !noParentCreate)
|
||||
{
|
||||
String *const pathParent = strPath(path);
|
||||
|
||||
storageInterfacePathCreateP(this, pathParent, errorOnExists, noParentCreate, mode);
|
||||
storageInterfacePathCreateP(this, path, errorOnExists, noParentCreate, mode);
|
||||
|
||||
strFree(pathParent);
|
||||
}
|
||||
else if (sftpErrno != LIBSSH2_FX_FILE_ALREADY_EXISTS || errorOnExists)
|
||||
THROW_FMT(PathCreateError, "sftp error unable to create path '%s'", strZ(path));
|
||||
}
|
||||
else
|
||||
THROW_FMT(PathCreateError, "ssh2 error [%d] unable to create path '%s'", rc, strZ(path));
|
||||
}
|
||||
|
||||
FUNCTION_LOG_RETURN_VOID();
|
||||
}
|
||||
|
||||
static bool
|
||||
storageSftpPathRemove(THIS_VOID, const String *const path, const bool recurse, const StorageInterfacePathRemoveParam param)
|
||||
{
|
||||
THIS(StorageSftp);
|
||||
|
||||
FUNCTION_LOG_BEGIN(logLevelTrace);
|
||||
FUNCTION_LOG_PARAM(STORAGE_SFTP, this);
|
||||
FUNCTION_LOG_PARAM(STRING, path);
|
||||
FUNCTION_LOG_PARAM(BOOL, recurse);
|
||||
(void)param; // No parameters are used
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
ASSERT(this != NULL);
|
||||
ASSERT(path != NULL);
|
||||
|
||||
bool result = true;
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
// Recurse if requested
|
||||
if (recurse)
|
||||
{
|
||||
StorageList *const list = storageInterfaceListP(this, path, storageInfoLevelExists);
|
||||
|
||||
if (list != NULL)
|
||||
{
|
||||
MEM_CONTEXT_TEMP_RESET_BEGIN()
|
||||
{
|
||||
for (unsigned int listIdx = 0; listIdx < storageLstSize(list); listIdx++)
|
||||
{
|
||||
const String *const file = strNewFmt("%s/%s", strZ(path), strZ(storageLstGet(list, listIdx).name));
|
||||
|
||||
// Rather than stat the file to discover what type it is, just try to unlink it and see what happens
|
||||
int rc;
|
||||
Wait *const wait = waitNew(this->timeout);
|
||||
|
||||
do
|
||||
{
|
||||
rc = libssh2_sftp_unlink_ex(this->sftpSession, strZ(file), (unsigned int)strSize(file));
|
||||
}
|
||||
while (rc == LIBSSH2_ERROR_EAGAIN && waitMore(wait));
|
||||
|
||||
waitFree(wait);
|
||||
|
||||
if (rc != 0)
|
||||
{
|
||||
// Attempting to unlink a directory appears to return LIBSSH2_FX_FAILURE
|
||||
if (rc == LIBSSH2_ERROR_SFTP_PROTOCOL &&
|
||||
libssh2_sftp_last_error(this->sftpSession) == LIBSSH2_FX_FAILURE)
|
||||
{
|
||||
storageInterfacePathRemoveP(this, file, true);
|
||||
}
|
||||
else
|
||||
THROW_FMT(PathRemoveError, STORAGE_ERROR_PATH_REMOVE_FILE, strZ(file));
|
||||
}
|
||||
|
||||
// Reset the memory context occasionally so we don't use too much memory or slow down processing
|
||||
MEM_CONTEXT_TEMP_RESET(1000);
|
||||
}
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the path
|
||||
int rc;
|
||||
Wait *const wait = waitNew(this->timeout);
|
||||
|
||||
do
|
||||
{
|
||||
rc = libssh2_sftp_rmdir_ex(this->sftpSession, strZ(path), (unsigned int)strSize(path));
|
||||
}
|
||||
while (rc == LIBSSH2_ERROR_EAGAIN && waitMore(wait));
|
||||
|
||||
waitFree(wait);
|
||||
|
||||
if (rc != 0)
|
||||
{
|
||||
if (rc == LIBSSH2_ERROR_SFTP_PROTOCOL)
|
||||
{
|
||||
if (libssh2_sftp_last_error(this->sftpSession) != LIBSSH2_FX_NO_SUCH_FILE)
|
||||
THROW_FMT(PathRemoveError, STORAGE_ERROR_PATH_REMOVE, strZ(path));
|
||||
|
||||
// Path does not exist
|
||||
result = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Path does not exist
|
||||
result = false;
|
||||
|
||||
THROW_FMT(PathRemoveError, STORAGE_ERROR_PATH_REMOVE, strZ(path));
|
||||
}
|
||||
}
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
FUNCTION_LOG_RETURN(BOOL, result);
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
static const StorageInterface storageInterfaceSftp =
|
||||
{
|
||||
.feature = 1 << storageFeaturePath | 1 << storageFeatureInfoDetail,
|
||||
|
||||
.info = storageSftpInfo,
|
||||
.list = storageSftpList,
|
||||
.newRead = storageSftpNewRead,
|
||||
.newWrite = storageSftpNewWrite,
|
||||
.pathCreate = storageSftpPathCreate,
|
||||
.pathRemove = storageSftpPathRemove,
|
||||
.remove = storageSftpRemove,
|
||||
};
|
||||
|
||||
FN_EXTERN Storage *
|
||||
storageSftpNew(
|
||||
const String *const path, const String *const host, const unsigned int port, const String *const user,
|
||||
const TimeMSec timeout, const String *const keyPriv, const StringId hostKeyHashType, const StorageSftpNewParam param)
|
||||
{
|
||||
FUNCTION_LOG_BEGIN(logLevelDebug);
|
||||
FUNCTION_LOG_PARAM(STRING, path);
|
||||
FUNCTION_LOG_PARAM(STRING, host);
|
||||
FUNCTION_LOG_PARAM(UINT, port);
|
||||
FUNCTION_LOG_PARAM(STRING, user);
|
||||
FUNCTION_LOG_PARAM(TIME_MSEC, timeout);
|
||||
FUNCTION_LOG_PARAM(STRING, keyPriv);
|
||||
FUNCTION_LOG_PARAM(STRING_ID, hostKeyHashType);
|
||||
FUNCTION_LOG_PARAM(STRING, param.keyPub);
|
||||
FUNCTION_TEST_PARAM(STRING, param.keyPassphrase);
|
||||
FUNCTION_LOG_PARAM(STRING, param.hostFingerprint);
|
||||
FUNCTION_LOG_PARAM(MODE, param.modeFile);
|
||||
FUNCTION_LOG_PARAM(MODE, param.modePath);
|
||||
FUNCTION_LOG_PARAM(BOOL, param.write);
|
||||
FUNCTION_LOG_PARAM(FUNCTIONP, param.pathExpressionFunction);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
ASSERT(path != NULL);
|
||||
ASSERT(host != NULL);
|
||||
ASSERT(port != 0);
|
||||
ASSERT(user != NULL);
|
||||
ASSERT(keyPriv != NULL);
|
||||
ASSERT(hostKeyHashType != 0);
|
||||
|
||||
// Initialize user module
|
||||
userInit();
|
||||
|
||||
// Create the object
|
||||
OBJ_NEW_BEGIN(StorageSftp, .childQty = MEM_CONTEXT_QTY_MAX, .callbackQty = 1)
|
||||
{
|
||||
*this = (StorageSftp)
|
||||
{
|
||||
.interface = storageInterfaceSftp,
|
||||
.timeout = timeout,
|
||||
};
|
||||
|
||||
if (libssh2_init(0) != 0)
|
||||
THROW_FMT(ServiceError, "unable to init libssh2");
|
||||
|
||||
this->ioSession = ioClientOpen(sckClientNew(host, port, timeout, timeout));
|
||||
|
||||
this->session = libssh2_session_init();
|
||||
if (this->session == NULL)
|
||||
THROW_FMT(ServiceError, "unable to init libssh2 session");
|
||||
|
||||
// Returns void
|
||||
libssh2_session_set_blocking(this->session, 0);
|
||||
|
||||
int handshakeStatus = 0;
|
||||
Wait *wait = waitNew(timeout);
|
||||
|
||||
do
|
||||
{
|
||||
handshakeStatus = libssh2_session_handshake(this->session, ioSessionFd(this->ioSession));
|
||||
}
|
||||
while (handshakeStatus == LIBSSH2_ERROR_EAGAIN && waitMore(wait));
|
||||
|
||||
waitFree(wait);
|
||||
|
||||
if (handshakeStatus != 0)
|
||||
THROW_FMT(ServiceError, "libssh2 handshake failed [%d]", handshakeStatus);
|
||||
|
||||
int hashType = LIBSSH2_HOSTKEY_HASH_SHA1;
|
||||
size_t hashSize = 0;
|
||||
|
||||
// Verify that the fingerprint[N] buffer declared below is large enough when adding a new hashType
|
||||
switch (hostKeyHashType)
|
||||
{
|
||||
case hashTypeMd5:
|
||||
hashType = LIBSSH2_HOSTKEY_HASH_MD5;
|
||||
hashSize = HASH_TYPE_M5_SIZE;
|
||||
break;
|
||||
|
||||
case hashTypeSha1:
|
||||
hashType = LIBSSH2_HOSTKEY_HASH_SHA1;
|
||||
hashSize = HASH_TYPE_SHA1_SIZE;
|
||||
break;
|
||||
|
||||
#ifdef LIBSSH2_HOSTKEY_HASH_SHA256
|
||||
case hashTypeSha256:
|
||||
hashType = LIBSSH2_HOSTKEY_HASH_SHA256;
|
||||
hashSize = HASH_TYPE_SHA256_SIZE;
|
||||
break;
|
||||
#endif // LIBSSH2_HOSTKEY_HASH_SHA256
|
||||
|
||||
default:
|
||||
THROW_FMT(
|
||||
ServiceError, "requested ssh2 hostkey hash type (%s) not available", strZ(strIdToStr(hostKeyHashType)));
|
||||
break;
|
||||
}
|
||||
|
||||
const char *binaryFingerprint = libssh2_hostkey_hash(this->session, hashType);
|
||||
|
||||
if (binaryFingerprint == NULL)
|
||||
THROW_FMT(ServiceError, "libssh2 hostkey hash failed: libssh2 errno [%d]", libssh2_session_last_errno(this->session));
|
||||
|
||||
// Compare fingerprint if provided
|
||||
if (param.hostFingerprint != NULL)
|
||||
{
|
||||
// 256 bytes is large enough to hold the hex representation of currently supported hash types. The hex encoded version
|
||||
// requires twice as much space (hashSize * 2) as the raw version.
|
||||
char fingerprint[256];
|
||||
|
||||
encodeToStr(encodingHex, (unsigned char *)binaryFingerprint, hashSize, fingerprint);
|
||||
|
||||
if (strcmp(fingerprint, strZ(param.hostFingerprint)) != 0)
|
||||
{
|
||||
THROW_FMT(
|
||||
ServiceError, "host [%s] and configured fingerprint (repo-sftp-host-fingerprint) [%s] do not match",
|
||||
fingerprint, strZ(param.hostFingerprint));
|
||||
}
|
||||
}
|
||||
|
||||
LOG_DEBUG_FMT("attempting public key authentication");
|
||||
|
||||
int rc;
|
||||
wait = waitNew(timeout);
|
||||
|
||||
do
|
||||
{
|
||||
rc = libssh2_userauth_publickey_fromfile(
|
||||
this->session, strZ(user), strZNull(param.keyPub), strZ(keyPriv), strZNull(param.keyPassphrase));
|
||||
}
|
||||
while (rc == LIBSSH2_ERROR_EAGAIN && waitMore(wait));
|
||||
|
||||
waitFree(wait);
|
||||
|
||||
if (rc != 0)
|
||||
{
|
||||
storageSftpEvalLibSsh2Error(
|
||||
rc, libssh2_sftp_last_error(this->sftpSession), &ServiceError,
|
||||
STRDEF("public key authentication failed"),
|
||||
STRDEF(
|
||||
"HINT: libssh2 compiled against non-openssl libraries requires --repo-sftp-private-key-file and"
|
||||
" --repo-sftp-public-key-file to be provided\n"
|
||||
"HINT: libssh2 versions before 1.9.0 expect a PEM format keypair, try ssh-keygen -m PEM -t rsa -P \"\" to"
|
||||
" generate the keypair"));
|
||||
}
|
||||
|
||||
wait = waitNew(timeout);
|
||||
|
||||
do
|
||||
{
|
||||
this->sftpSession = libssh2_sftp_init(this->session);
|
||||
}
|
||||
while (this->sftpSession == NULL && waitMore(wait));
|
||||
|
||||
waitFree(wait);
|
||||
|
||||
if (this->sftpSession == NULL)
|
||||
THROW_FMT(ServiceError, "unable to init libssh2_sftp session");
|
||||
|
||||
// Ensure libssh2/libssh2_sftp resources freed
|
||||
memContextCallbackSet(objMemContext(this), storageSftpLibSsh2SessionFreeResource, this);
|
||||
}
|
||||
OBJ_NEW_END();
|
||||
|
||||
FUNCTION_LOG_RETURN(
|
||||
STORAGE,
|
||||
storageNew(
|
||||
STORAGE_SFTP_TYPE, path, param.modeFile == 0 ? STORAGE_MODE_FILE_DEFAULT : param.modeFile,
|
||||
param.modePath == 0 ? STORAGE_MODE_PATH_DEFAULT : param.modePath, param.write, param.pathExpressionFunction,
|
||||
this, this->interface));
|
||||
}
|
||||
|
||||
#endif // HAVE_LIBSSH2
|
40
src/storage/sftp/storage.h
Normal file
40
src/storage/sftp/storage.h
Normal file
@ -0,0 +1,40 @@
|
||||
/***********************************************************************************************************************************
|
||||
SFTP Storage
|
||||
***********************************************************************************************************************************/
|
||||
#ifndef STORAGE_SFTP_STORAGE_H
|
||||
#define STORAGE_SFTP_STORAGE_H
|
||||
|
||||
#include "storage/storage.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Storage type
|
||||
***********************************************************************************************************************************/
|
||||
#define STORAGE_SFTP_TYPE STRID5("sftp", 0x850d30)
|
||||
|
||||
#ifdef HAVE_LIBSSH2
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Constructors
|
||||
***********************************************************************************************************************************/
|
||||
typedef struct StorageSftpNewParam
|
||||
{
|
||||
VAR_PARAM_HEADER;
|
||||
bool write;
|
||||
mode_t modeFile;
|
||||
mode_t modePath;
|
||||
StoragePathExpressionCallback *pathExpressionFunction;
|
||||
const String *keyPub;
|
||||
const String *keyPassphrase;
|
||||
const String *hostFingerprint;
|
||||
} StorageSftpNewParam;
|
||||
|
||||
#define storageSftpNewP(path, host, port, user, timeout, keyPriv, hostKeyHashType, ...) \
|
||||
storageSftpNew(path, host, port, user, timeout, keyPriv, hostKeyHashType, (StorageSftpNewParam){VAR_PARAM_INIT, __VA_ARGS__})
|
||||
|
||||
FN_EXTERN Storage *storageSftpNew(
|
||||
const String *path, const String *host, unsigned int port, const String *user, TimeMSec timeout, const String *keyPriv,
|
||||
StringId hostKeyHashType, const StorageSftpNewParam param);
|
||||
|
||||
#endif // HAVE_LIBSSH2
|
||||
|
||||
#endif
|
31
src/storage/sftp/storage.intern.h
Normal file
31
src/storage/sftp/storage.intern.h
Normal file
@ -0,0 +1,31 @@
|
||||
/***********************************************************************************************************************************
|
||||
SFTP Storage Internal
|
||||
***********************************************************************************************************************************/
|
||||
#ifndef STORAGE_SFTP_STORAGE_INTERN_H
|
||||
#define STORAGE_SFTP_STORAGE_INTERN_H
|
||||
|
||||
#include <libssh2.h>
|
||||
#include <libssh2_sftp.h>
|
||||
|
||||
#include "storage/sftp/storage.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Object type
|
||||
***********************************************************************************************************************************/
|
||||
typedef struct StorageSftp StorageSftp;
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Functions
|
||||
***********************************************************************************************************************************/
|
||||
FN_EXTERN void storageSftpEvalLibSsh2Error(
|
||||
int ssh2Errno, uint64_t sftpErrno, const ErrorType *errorType, const String *msg, const String *hint);
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Macros for function logging
|
||||
***********************************************************************************************************************************/
|
||||
#define FUNCTION_LOG_STORAGE_SFTP_TYPE \
|
||||
StorageSftp *
|
||||
#define FUNCTION_LOG_STORAGE_SFTP_FORMAT(value, buffer, bufferSize) \
|
||||
objNameToLog(value, "StorageSftp *", buffer, bufferSize)
|
||||
|
||||
#endif
|
414
src/storage/sftp/write.c
Normal file
414
src/storage/sftp/write.c
Normal file
@ -0,0 +1,414 @@
|
||||
/***********************************************************************************************************************************
|
||||
SFTP Storage File Write
|
||||
***********************************************************************************************************************************/
|
||||
#include "build.auto.h"
|
||||
|
||||
#ifdef HAVE_LIBSSH2
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/log.h"
|
||||
#include "common/user.h"
|
||||
#include "common/wait.h"
|
||||
#include "storage/sftp/write.h"
|
||||
#include "storage/write.intern.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Object type
|
||||
***********************************************************************************************************************************/
|
||||
typedef struct StorageWriteSftp
|
||||
{
|
||||
StorageWriteInterface interface; // Interface
|
||||
StorageSftp *storage; // Storage that created this object
|
||||
|
||||
const String *nameTmp; // Temporary filename utilized for atomic ops
|
||||
const String *path; // Utilized for path operations
|
||||
IoSession *ioSession; // IoSession (socket) connection to SFTP server
|
||||
LIBSSH2_SESSION *session; // LibSsh2 session
|
||||
LIBSSH2_SFTP *sftpSession; // LibSsh2 session sftp session
|
||||
LIBSSH2_SFTP_HANDLE *sftpHandle; // LibSsh2 session sftp handle
|
||||
TimeMSec timeout; // Session timeout
|
||||
} StorageWriteSftp;
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Macros for function logging
|
||||
***********************************************************************************************************************************/
|
||||
#define FUNCTION_LOG_STORAGE_WRITE_SFTP_TYPE \
|
||||
StorageWriteSftp *
|
||||
#define FUNCTION_LOG_STORAGE_WRITE_SFTP_FORMAT(value, buffer, bufferSize) \
|
||||
objNameToLog(value, "StorageWriteSftp", buffer, bufferSize)
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Open the file
|
||||
***********************************************************************************************************************************/
|
||||
static void
|
||||
storageWriteSftpOpen(THIS_VOID)
|
||||
{
|
||||
THIS(StorageWriteSftp);
|
||||
|
||||
FUNCTION_LOG_BEGIN(logLevelTrace);
|
||||
FUNCTION_LOG_PARAM(STORAGE_WRITE_SFTP, this);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
ASSERT(this != NULL);
|
||||
ASSERT(this->sftpSession != NULL);
|
||||
|
||||
const unsigned long int flags = LIBSSH2_FXF_CREAT | LIBSSH2_FXF_WRITE | LIBSSH2_FXF_TRUNC;
|
||||
|
||||
// Open the file
|
||||
Wait *const wait = waitNew(this->timeout);
|
||||
|
||||
do
|
||||
{
|
||||
this->sftpHandle = libssh2_sftp_open_ex(
|
||||
this->sftpSession, strZ(this->nameTmp), (unsigned int)strSize(this->nameTmp), flags, (int)this->interface.modeFile,
|
||||
LIBSSH2_SFTP_OPENFILE);
|
||||
}
|
||||
while (this->sftpHandle == NULL && libssh2_session_last_errno(this->session) == LIBSSH2_ERROR_EAGAIN && waitMore(wait));
|
||||
|
||||
waitFree(wait);
|
||||
|
||||
// Attempt to create the path if it is missing
|
||||
if (this->sftpHandle == NULL && libssh2_session_last_errno(this->session) == LIBSSH2_ERROR_SFTP_PROTOCOL &&
|
||||
libssh2_sftp_last_error(this->sftpSession) == LIBSSH2_FX_NO_SUCH_FILE)
|
||||
{
|
||||
// Create the path
|
||||
storageInterfacePathCreateP(this->storage, this->path, false, false, this->interface.modePath);
|
||||
|
||||
// Open file again
|
||||
Wait *const wait = waitNew(this->timeout);
|
||||
|
||||
do
|
||||
{
|
||||
this->sftpHandle = libssh2_sftp_open_ex(
|
||||
this->sftpSession, strZ(this->nameTmp), (unsigned int)strSize(this->nameTmp), flags, (int)this->interface.modeFile,
|
||||
LIBSSH2_SFTP_OPENFILE);
|
||||
}
|
||||
while (this->sftpHandle == NULL && (libssh2_session_last_errno(this->session) == LIBSSH2_ERROR_EAGAIN && waitMore(wait)));
|
||||
|
||||
waitFree(wait);
|
||||
}
|
||||
|
||||
// Handle error
|
||||
if (this->sftpHandle == NULL)
|
||||
{
|
||||
const int rc = libssh2_session_last_errno(this->session);
|
||||
|
||||
if (rc == LIBSSH2_ERROR_SFTP_PROTOCOL)
|
||||
{
|
||||
uint64_t sftpErr = libssh2_sftp_last_error(this->sftpSession);
|
||||
|
||||
if (sftpErr == LIBSSH2_FX_NO_SUCH_FILE)
|
||||
THROW_FMT(FileMissingError, STORAGE_ERROR_WRITE_MISSING, strZ(this->interface.name));
|
||||
else
|
||||
{
|
||||
storageSftpEvalLibSsh2Error(
|
||||
rc, sftpErr, &FileOpenError, strNewFmt(STORAGE_ERROR_WRITE_OPEN, strZ(this->interface.name)), NULL);
|
||||
}
|
||||
}
|
||||
else
|
||||
THROW_FMT(FileOpenError, STORAGE_ERROR_WRITE_OPEN, strZ(this->interface.name));
|
||||
}
|
||||
|
||||
FUNCTION_LOG_RETURN_VOID();
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Write to the file
|
||||
***********************************************************************************************************************************/
|
||||
static void
|
||||
storageWriteSftp(THIS_VOID, const Buffer *const buffer)
|
||||
{
|
||||
THIS(StorageWriteSftp);
|
||||
|
||||
FUNCTION_LOG_BEGIN(logLevelTrace);
|
||||
FUNCTION_LOG_PARAM(STORAGE_WRITE_SFTP, this);
|
||||
FUNCTION_LOG_PARAM(BUFFER, buffer);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
ASSERT(this != NULL);
|
||||
ASSERT(buffer != NULL);
|
||||
ASSERT(this->sftpHandle != NULL);
|
||||
|
||||
ssize_t rc;
|
||||
size_t remains = bufUsed(buffer); // Amount left to write
|
||||
size_t offset = 0; // Offset into the buffer
|
||||
|
||||
// Loop until all the data is written
|
||||
do
|
||||
{
|
||||
Wait *const wait = waitNew(this->timeout);
|
||||
|
||||
do
|
||||
{
|
||||
rc = libssh2_sftp_write(this->sftpHandle, (const char *)bufPtrConst(buffer) + offset, remains);
|
||||
}
|
||||
while (rc == LIBSSH2_ERROR_EAGAIN && waitMore(wait));
|
||||
|
||||
waitFree(wait);
|
||||
|
||||
// Break on error. Error will be thrown below the loop.
|
||||
if (rc < 0)
|
||||
break;
|
||||
|
||||
// Offset for next write start point
|
||||
offset += (size_t)rc;
|
||||
|
||||
// Update amount left to write
|
||||
remains -= (size_t)rc;
|
||||
}
|
||||
while (remains);
|
||||
|
||||
if (rc < 0)
|
||||
THROW_FMT(FileWriteError, "unable to write '%s'", strZ(this->nameTmp));
|
||||
|
||||
FUNCTION_LOG_RETURN_VOID();
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Unlink already existing file
|
||||
***********************************************************************************************************************************/
|
||||
static void
|
||||
storageWriteSftpUnlinkExisting(THIS_VOID)
|
||||
{
|
||||
THIS(StorageWriteSftp);
|
||||
|
||||
FUNCTION_LOG_BEGIN(logLevelTrace);
|
||||
FUNCTION_LOG_PARAM(STORAGE_WRITE_SFTP, this);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
ASSERT(this != NULL);
|
||||
|
||||
int rc;
|
||||
Wait *const wait = waitNew(this->timeout);
|
||||
|
||||
do
|
||||
{
|
||||
rc = libssh2_sftp_unlink_ex(this->sftpSession, strZ(this->interface.name), (unsigned int)strSize(this->interface.name));
|
||||
}
|
||||
while (rc == LIBSSH2_ERROR_EAGAIN && waitMore(wait));
|
||||
|
||||
waitFree(wait);
|
||||
|
||||
if (rc)
|
||||
{
|
||||
storageSftpEvalLibSsh2Error(
|
||||
rc, libssh2_sftp_last_error(this->sftpSession), &FileRemoveError,
|
||||
strNewFmt("unable to remove existing '%s'", strZ(this->interface.name)), NULL);
|
||||
}
|
||||
|
||||
FUNCTION_LOG_RETURN_VOID();
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Rename a file
|
||||
***********************************************************************************************************************************/
|
||||
static void
|
||||
storageWriteSftpRename(THIS_VOID)
|
||||
{
|
||||
THIS(StorageWriteSftp);
|
||||
|
||||
FUNCTION_LOG_BEGIN(logLevelTrace);
|
||||
FUNCTION_LOG_PARAM(STORAGE_WRITE_SFTP, this);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
ASSERT(this != NULL);
|
||||
|
||||
int rc;
|
||||
Wait *const wait = waitNew(this->timeout);
|
||||
|
||||
do
|
||||
{
|
||||
rc = libssh2_sftp_rename_ex(
|
||||
this->sftpSession, strZ(this->nameTmp), (unsigned int)strSize(this->nameTmp), strZ(this->interface.name),
|
||||
(unsigned int)strSize(this->interface.name),
|
||||
LIBSSH2_SFTP_RENAME_OVERWRITE | LIBSSH2_SFTP_RENAME_ATOMIC | LIBSSH2_SFTP_RENAME_NATIVE);
|
||||
}
|
||||
while (rc == LIBSSH2_ERROR_EAGAIN && waitMore(wait));
|
||||
|
||||
waitFree(wait);
|
||||
|
||||
if (rc)
|
||||
{
|
||||
storageSftpEvalLibSsh2Error(
|
||||
rc, libssh2_sftp_last_error(this->sftpSession), &FileRemoveError,
|
||||
strNewFmt("unable to move '%s' to '%s'", strZ(this->nameTmp), strZ(this->interface.name)), NULL);
|
||||
}
|
||||
|
||||
FUNCTION_LOG_RETURN_VOID();
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Close the file
|
||||
***********************************************************************************************************************************/
|
||||
static void
|
||||
storageWriteSftpClose(THIS_VOID)
|
||||
{
|
||||
THIS(StorageWriteSftp);
|
||||
|
||||
FUNCTION_LOG_BEGIN(logLevelTrace);
|
||||
FUNCTION_LOG_PARAM(STORAGE_WRITE_SFTP, this);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
ASSERT(this != NULL);
|
||||
|
||||
// Close if the file has not already been closed
|
||||
if (this->sftpHandle != NULL)
|
||||
{
|
||||
int rc;
|
||||
char *libSsh2ErrMsg;
|
||||
int errMsgLen;
|
||||
int libSsh2ErrNo;
|
||||
|
||||
if (this->interface.syncFile)
|
||||
{
|
||||
Wait *const wait = waitNew(this->timeout);
|
||||
|
||||
do
|
||||
{
|
||||
rc = libssh2_sftp_fsync(this->sftpHandle);
|
||||
}
|
||||
while (rc == LIBSSH2_ERROR_EAGAIN && waitMore(wait));
|
||||
|
||||
waitFree(wait);
|
||||
|
||||
if (rc)
|
||||
THROW_FMT(FileSyncError, STORAGE_ERROR_WRITE_SYNC, strZ(this->nameTmp));
|
||||
}
|
||||
|
||||
// Close the file
|
||||
Wait *const wait = waitNew(this->timeout);
|
||||
|
||||
do
|
||||
{
|
||||
rc = libssh2_sftp_close(this->sftpHandle);
|
||||
}
|
||||
while (rc == LIBSSH2_ERROR_EAGAIN && waitMore(wait));
|
||||
|
||||
waitFree(wait);
|
||||
|
||||
if (rc != 0)
|
||||
{
|
||||
libSsh2ErrNo = libssh2_session_last_error(this->session, &libSsh2ErrMsg, &errMsgLen, 0);
|
||||
|
||||
THROW_FMT(
|
||||
FileCloseError,
|
||||
STORAGE_ERROR_WRITE_CLOSE ": libssh2 error [%d] %s", strZ(this->nameTmp), libSsh2ErrNo, libSsh2ErrMsg);
|
||||
}
|
||||
|
||||
this->sftpHandle = NULL;
|
||||
|
||||
// Rename from temp file
|
||||
if (this->interface.atomic)
|
||||
{
|
||||
Wait *const wait = waitNew(this->timeout);
|
||||
|
||||
do
|
||||
{
|
||||
rc = libssh2_sftp_rename_ex(
|
||||
this->sftpSession, strZ(this->nameTmp), (unsigned int)strSize(this->nameTmp), strZ(this->interface.name),
|
||||
(unsigned int)strSize(this->interface.name),
|
||||
LIBSSH2_SFTP_RENAME_OVERWRITE | LIBSSH2_SFTP_RENAME_ATOMIC | LIBSSH2_SFTP_RENAME_NATIVE);
|
||||
}
|
||||
while (rc == LIBSSH2_ERROR_EAGAIN && waitMore(wait));
|
||||
|
||||
waitFree(wait);
|
||||
|
||||
if (rc)
|
||||
{
|
||||
// Some/most sftp servers will not rename over an existing file, in testing this returned LIBSSH2_FX_FAILURE
|
||||
if (rc == LIBSSH2_ERROR_SFTP_PROTOCOL && libssh2_sftp_last_error(this->sftpSession) == LIBSSH2_FX_FAILURE)
|
||||
{
|
||||
// Remove the existing file and retry the rename
|
||||
storageWriteSftpUnlinkExisting(this);
|
||||
storageWriteSftpRename(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
storageSftpEvalLibSsh2Error(
|
||||
rc, libssh2_sftp_last_error(this->sftpSession), &FileCloseError,
|
||||
strNewFmt("unable to move '%s' to '%s'", strZ(this->nameTmp), strZ(this->interface.name)), NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FUNCTION_LOG_RETURN_VOID();
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
FN_EXTERN StorageWrite *
|
||||
storageWriteSftpNew(
|
||||
StorageSftp *const storage, const String *const name, IoSession *const ioSession, LIBSSH2_SESSION *const session,
|
||||
LIBSSH2_SFTP *const sftpSession, LIBSSH2_SFTP_HANDLE *const sftpHandle, const TimeMSec timeout, const mode_t modeFile,
|
||||
const mode_t modePath, const String *const user, const String *const group, const time_t timeModified, const bool createPath,
|
||||
const bool syncFile, const bool syncPath, const bool atomic, const bool truncate)
|
||||
{
|
||||
FUNCTION_LOG_BEGIN(logLevelTrace);
|
||||
FUNCTION_LOG_PARAM(STORAGE_SFTP, storage);
|
||||
FUNCTION_LOG_PARAM(STRING, name);
|
||||
FUNCTION_LOG_PARAM_P(VOID, session);
|
||||
FUNCTION_LOG_PARAM_P(VOID, sftpSession);
|
||||
FUNCTION_LOG_PARAM_P(VOID, sftpHandle);
|
||||
FUNCTION_LOG_PARAM(TIME_MSEC, timeout);
|
||||
FUNCTION_LOG_PARAM(MODE, modeFile);
|
||||
FUNCTION_LOG_PARAM(MODE, modePath);
|
||||
FUNCTION_LOG_PARAM(STRING, user);
|
||||
FUNCTION_LOG_PARAM(STRING, group);
|
||||
FUNCTION_LOG_PARAM(TIME, timeModified);
|
||||
FUNCTION_LOG_PARAM(BOOL, createPath);
|
||||
FUNCTION_LOG_PARAM(BOOL, syncFile);
|
||||
FUNCTION_LOG_PARAM(BOOL, syncPath);
|
||||
FUNCTION_LOG_PARAM(BOOL, atomic);
|
||||
FUNCTION_LOG_PARAM(BOOL, truncate);
|
||||
FUNCTION_LOG_END();
|
||||
|
||||
ASSERT(storage != NULL);
|
||||
ASSERT(name != NULL);
|
||||
ASSERT(modeFile != 0);
|
||||
ASSERT(modePath != 0);
|
||||
|
||||
OBJ_NEW_BEGIN(StorageWriteSftp, .childQty = MEM_CONTEXT_QTY_MAX)
|
||||
{
|
||||
*this = (StorageWriteSftp)
|
||||
{
|
||||
.storage = storage,
|
||||
.path = strPath(name),
|
||||
.ioSession = ioSession,
|
||||
.session = session,
|
||||
.sftpSession = sftpSession,
|
||||
.sftpHandle = sftpHandle,
|
||||
.timeout = timeout,
|
||||
|
||||
.interface = (StorageWriteInterface)
|
||||
{
|
||||
.type = STORAGE_SFTP_TYPE,
|
||||
.name = strDup(name),
|
||||
.atomic = atomic,
|
||||
.createPath = createPath,
|
||||
.group = strDup(group),
|
||||
.modeFile = modeFile,
|
||||
.modePath = modePath,
|
||||
.syncFile = syncFile,
|
||||
.syncPath = syncPath,
|
||||
.truncate = truncate,
|
||||
.user = strDup(user),
|
||||
.timeModified = timeModified,
|
||||
|
||||
.ioInterface = (IoWriteInterface)
|
||||
{
|
||||
.close = storageWriteSftpClose,
|
||||
.open = storageWriteSftpOpen,
|
||||
.write = storageWriteSftp,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Create temp file name
|
||||
this->nameTmp = atomic ? strNewFmt("%s." STORAGE_FILE_TEMP_EXT, strZ(name)) : this->interface.name;
|
||||
}
|
||||
OBJ_NEW_END();
|
||||
|
||||
FUNCTION_LOG_RETURN(STORAGE_WRITE, storageWriteNew(this, &this->interface));
|
||||
}
|
||||
|
||||
#endif // HAVE_LIBSSH2
|
19
src/storage/sftp/write.h
Normal file
19
src/storage/sftp/write.h
Normal file
@ -0,0 +1,19 @@
|
||||
/***********************************************************************************************************************************
|
||||
SFTP Storage File Write
|
||||
***********************************************************************************************************************************/
|
||||
#ifndef STORAGE_SFTP_WRITE_H
|
||||
#define STORAGE_SFTP_WRITE_H
|
||||
|
||||
#include "common/io/session.h"
|
||||
#include "storage/sftp/storage.h"
|
||||
#include "storage/sftp/storage.intern.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Constructors
|
||||
***********************************************************************************************************************************/
|
||||
FN_EXTERN StorageWrite *storageWriteSftpNew(
|
||||
StorageSftp *storage, const String *name, IoSession *ioSession, LIBSSH2_SESSION *session, LIBSSH2_SFTP *sftpSession,
|
||||
LIBSSH2_SFTP_HANDLE *sftpHandle, TimeMSec timeout, mode_t modeFile, mode_t modePath, const String *user, const String *group,
|
||||
time_t timeModified, bool createPath, bool syncFile, bool syncPath, bool atomic, bool truncate);
|
||||
|
||||
#endif
|
@ -13,7 +13,7 @@ RUN DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y \
|
||||
libdbd-pg-perl libxml-checker-perl libyaml-perl \
|
||||
devscripts build-essential lintian git cloc txt2man debhelper libssl-dev zlib1g-dev libperl-dev libxml2-dev liblz4-dev \
|
||||
liblz4-tool libpq-dev lcov autoconf-archive zstd libzstd-dev bzip2 libbz2-dev pkg-config libyaml-dev libc6-dbg wget meson \
|
||||
ccache valgrind tzdata uncrustify
|
||||
ccache valgrind tzdata uncrustify libssh2-1-dev
|
||||
|
||||
# Install Docker
|
||||
RUN groupadd -g5000 docker
|
||||
|
2
test/Vagrantfile
vendored
2
test/Vagrantfile
vendored
@ -76,7 +76,7 @@ Vagrant.configure(2) do |config|
|
||||
echo 'Install Build Tools' && date
|
||||
apt-get install -y devscripts build-essential lintian git cloc txt2man debhelper libssl-dev zlib1g-dev libperl-dev \
|
||||
libxml2-dev liblz4-dev liblz4-tool libpq-dev lcov autoconf-archive zstd libzstd-dev bzip2 libbz2-dev pkg-config \
|
||||
libyaml-dev libc6-dbg valgrind meson ccache uncrustify
|
||||
libyaml-dev libc6-dbg valgrind meson ccache uncrustify libssh2-1-dev
|
||||
|
||||
#-----------------------------------------------------------------------------------------------------------------------
|
||||
echo 'Install Docker' && date
|
||||
|
16
test/certificate/ssh/id_rsa
Normal file
16
test/certificate/ssh/id_rsa
Normal file
@ -0,0 +1,16 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIICXwIBAAKBgQDR0yJsZW5d5LcqteiOtv8d+FFeFFHDPI0VTcTOdMn1iDiIP1ou
|
||||
X3Q2OyNjsBaDbsRJd+sp9IRq1LKX3zsBcgGZANwm0zduuNEPEU94ajS/uRoejIqY
|
||||
/XkKOpnEF6ZbQ2S7TaE4sWeGLvba7kUFs0QTOO+N+nV2dMbdqZf6C8lazwIDAQAB
|
||||
AoGBAJXa6xzrnFVmwgK5BKzYuX/YF5TPgk2j80ch0ct50buQXH/Cb0/rUH5i4jWS
|
||||
T6Hy/DFUehnuzpvV6O9auTOhDs3BhEKFRuRLn1nBwTtZny5Hh+cw7azUCEHFCJlz
|
||||
makCrVbgawtno6oU/pFgQm1FcxD0f+Me5ruNcLHqUZsPQwkRAkEA+8pG+ckOlz6R
|
||||
AJLIHedmfcrEY9T7sfdo83bzMOz8H5soUUP4aOTLJYCla1LO7JdDnXMGo0KxaHBP
|
||||
l8j5zDmVewJBANVVPDJr1w37m0FBi37QgUOAijVfLXgyPMxYp2uc9ddjncif0063
|
||||
0Wc0FQefoPszf3CDrHv/RHvhHq97jXDwTb0CQQDgH83NygoS1r57pCw9chzpG/R0
|
||||
aMEiSPhCvz757fj+qT3aGIal2AJ7/2c/gRZvwrWNETZ3XIZOUKqIkXzJLPjBAkEA
|
||||
wnP799W2Y8d4/+VX2pMBkF7lG7sSviHEq1sP2BZtPBRQKSQNvw3scM7XcGh/mxmY
|
||||
yx0qpqfKa8SKbNgI1+4iXQJBAOlg8MJLwkUtrG+p8wf69oCuZsnyv0K6UMDxm6/8
|
||||
cbvfmvODulYFaIahaqHWEZoRo5CLYZ7gN43WHPOrKxdDL78=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
|
1
test/certificate/ssh/id_rsa.pub
Normal file
1
test/certificate/ssh/id_rsa.pub
Normal file
@ -0,0 +1 @@
|
||||
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDR0yJsZW5d5LcqteiOtv8d+FFeFFHDPI0VTcTOdMn1iDiIP1ouX3Q2OyNjsBaDbsRJd+sp9IRq1LKX3zsBcgGZANwm0zduuNEPEU94ajS/uRoejIqY/XkKOpnEF6ZbQ2S7TaE4sWeGLvba7kUFs0QTOO+N+nV2dMbdqZf6C8lazw==
|
@ -189,7 +189,7 @@ eval
|
||||
# Build list of packages that need to be installed
|
||||
my $strPackage =
|
||||
"make gcc ccache meson python3-pip git rsync zlib1g-dev libssl-dev libxml2-dev libpq-dev libyaml-dev pkg-config" .
|
||||
" uncrustify";
|
||||
" uncrustify libssh2-1-dev";
|
||||
|
||||
# Add lcov when testing coverage
|
||||
if (vmCoverageC($strVm))
|
||||
|
@ -1859,6 +1859,42 @@ src/storage/s3/write.h:
|
||||
class: core
|
||||
type: c/h
|
||||
|
||||
src/storage/sftp/helper.c:
|
||||
class: core
|
||||
type: c
|
||||
|
||||
src/storage/sftp/helper.h:
|
||||
class: core
|
||||
type: c/h
|
||||
|
||||
src/storage/sftp/read.c:
|
||||
class: core
|
||||
type: c
|
||||
|
||||
src/storage/sftp/read.h:
|
||||
class: core
|
||||
type: c/h
|
||||
|
||||
src/storage/sftp/storage.c:
|
||||
class: core
|
||||
type: c
|
||||
|
||||
src/storage/sftp/storage.h:
|
||||
class: core
|
||||
type: c/h
|
||||
|
||||
src/storage/sftp/storage.intern.h:
|
||||
class: core
|
||||
type: c/h
|
||||
|
||||
src/storage/sftp/write.c:
|
||||
class: core
|
||||
type: c
|
||||
|
||||
src/storage/sftp/write.h:
|
||||
class: core
|
||||
type: c/h
|
||||
|
||||
src/storage/storage.c:
|
||||
class: core
|
||||
type: c
|
||||
@ -2163,6 +2199,22 @@ test/src/common/harnessInfo.h:
|
||||
class: test/harness
|
||||
type: c/h
|
||||
|
||||
test/src/common/harnessIo.c:
|
||||
class: test/harness
|
||||
type: c
|
||||
|
||||
test/src/common/harnessIo.h:
|
||||
class: test/harness
|
||||
type: c/h
|
||||
|
||||
test/src/common/harnessLibssh2.c:
|
||||
class: test/harness
|
||||
type: c
|
||||
|
||||
test/src/common/harnessLibssh2.h:
|
||||
class: test/harness
|
||||
type: c/h
|
||||
|
||||
test/src/common/harnessLog.c:
|
||||
class: test/harness
|
||||
type: c
|
||||
@ -2639,6 +2691,10 @@ test/src/module/storage/s3Test.c:
|
||||
class: test/module
|
||||
type: c
|
||||
|
||||
test/src/module/storage/sftpTest.c:
|
||||
class: test/module
|
||||
type: c
|
||||
|
||||
test/src/module/test/testTest.c:
|
||||
class: test/module
|
||||
type: c
|
||||
|
@ -12,19 +12,9 @@
|
||||
# - docker login -u pgbackrest
|
||||
# - VM=XXX;DATE=YYYYMMDDX;BASE=pgbackrest/test:${VM?}-base;docker tag ${BASE?} ${BASE?}-${DATE?} && docker push ${BASE?}-${DATE?}
|
||||
# **********************************************************************************************************************************
|
||||
20230427A:
|
||||
20230513A:
|
||||
x86_64:
|
||||
u22: 6a07d63ae869c3d84d1bf6622826df9fe768691d
|
||||
|
||||
20230405A:
|
||||
x86_64:
|
||||
d10: 01a943f8d68ee5bbb2a5ee10cc474925c6cc0cde
|
||||
|
||||
20221220A:
|
||||
x86_64:
|
||||
u20: 2db467d873c0aff06335592c8a22b8441b5c6440
|
||||
|
||||
20220726A:
|
||||
x86_64:
|
||||
f36: 099b329ca7988b05f2cb8ef759e146ea9faab108
|
||||
rh7: 6072f05804b369681efad5cebe01704cb9d2a81a
|
||||
d10: 633def5323eeed51d7aa187ad1d61e854c47d6fa
|
||||
f36: 640d3ed0d9786ef61263e27a77686a331f72c58e
|
||||
rh7: 18dd0cbe19aa66dd9d72d312da5cb8c4bddea8b9
|
||||
u22: eba71d24c8636c7dfea5942f78a82a4566bef784
|
||||
|
@ -365,6 +365,12 @@ unit:
|
||||
total: 5
|
||||
feature: SOCKET
|
||||
harness: server
|
||||
harness:
|
||||
name: socket
|
||||
shim:
|
||||
common/io/socket/client:
|
||||
function:
|
||||
- sckClientOpen
|
||||
|
||||
coverage:
|
||||
- common/io/client
|
||||
@ -598,6 +604,23 @@ unit:
|
||||
- storage/storage
|
||||
- storage/write
|
||||
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
- name: sftp
|
||||
total: 20
|
||||
harness: libSsh2
|
||||
|
||||
coverage:
|
||||
- storage/sftp/helper
|
||||
- storage/sftp/read
|
||||
- storage/sftp/storage
|
||||
- storage/sftp/write
|
||||
|
||||
include:
|
||||
- storage/helper
|
||||
- storage/read
|
||||
- storage/storage
|
||||
- storage/write
|
||||
|
||||
# ********************************************************************************************************************************
|
||||
- name: postgres
|
||||
|
||||
|
@ -215,6 +215,7 @@ sub sshSetup
|
||||
}
|
||||
|
||||
$strScript .=
|
||||
" cp ${strUserPath}/.ssh/authorized_keys ${strUserPath}/.ssh/id_rsa.pub && \\\n" .
|
||||
" chown -R ${strUser}:${strGroup} ${strUserPath}/.ssh && \\\n" .
|
||||
" chmod 700 ${strUserPath}/.ssh && \\\n" .
|
||||
" chmod 600 ${strUserPath}/.ssh/*";
|
||||
@ -385,7 +386,8 @@ sub containerBuild
|
||||
" yum -y install openssh-server openssh-clients wget sudo valgrind git \\\n" .
|
||||
" perl perl-Digest-SHA perl-DBD-Pg perl-YAML-LibYAML openssl \\\n" .
|
||||
" gcc make perl-ExtUtils-MakeMaker perl-Test-Simple openssl-devel perl-ExtUtils-Embed rpm-build \\\n" .
|
||||
" libyaml-devel zlib-devel libxml2-devel lz4-devel lz4 bzip2-devel bzip2 perl-JSON-PP ccache meson";
|
||||
" libyaml-devel zlib-devel libxml2-devel lz4-devel lz4 bzip2-devel bzip2 perl-JSON-PP ccache meson \\\n" .
|
||||
" libssh2-devel";
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -396,7 +398,8 @@ sub containerBuild
|
||||
" libdbd-pg-perl libhtml-parser-perl libssl-dev libperl-dev \\\n" .
|
||||
" libyaml-libyaml-perl tzdata devscripts lintian libxml-checker-perl txt2man debhelper \\\n" .
|
||||
" libppi-html-perl libtemplate-perl libtest-differences-perl zlib1g-dev libxml2-dev pkg-config \\\n" .
|
||||
" libbz2-dev bzip2 libyaml-dev libjson-pp-perl liblz4-dev liblz4-tool gnupg lsb-release ccache meson";
|
||||
" libbz2-dev bzip2 libyaml-dev libjson-pp-perl liblz4-dev liblz4-tool gnupg lsb-release ccache meson \\\n" .
|
||||
" libssh2-1-dev";
|
||||
|
||||
# This package is required to build valgrind on 32-bit
|
||||
if ($oVm->{$strOS}{&VM_ARCH} eq VM_ARCH_I386)
|
||||
@ -612,6 +615,14 @@ sub containerBuild
|
||||
" echo '***********************************************' >> /etc/issue.net && \\\n" .
|
||||
" echo 'Banner /etc/issue.net' >> /etc/ssh/sshd_config";
|
||||
|
||||
if ($strOS eq VM_U22)
|
||||
{
|
||||
$strScript .= sectionHeader() .
|
||||
" echo '# Add PubkeyAcceptedAlgorithms (required for SFTP)' >> /etc/ssh/sshd_config && \\\n" .
|
||||
" echo 'HostKeyAlgorithms=+ssh-rsa,ssh-rsa-cert-v01\@openssh.com' >> /etc/ssh/sshd_config && \\\n" .
|
||||
" echo 'PubkeyAcceptedAlgorithms=+ssh-rsa,ssh-rsa-cert-v01\@openssh.com' >> /etc/ssh/sshd_config";
|
||||
}
|
||||
|
||||
$strScript .= sectionHeader() .
|
||||
"# Create test user\n" .
|
||||
' ' . groupCreate($strOS, TEST_GROUP, TEST_GROUP_ID) . " && \\\n" .
|
||||
|
@ -162,16 +162,15 @@ my $oyVm =
|
||||
|
||||
&VM_DB =>
|
||||
[
|
||||
PG_VERSION_10,
|
||||
PG_VERSION_11,
|
||||
PG_VERSION_12,
|
||||
PG_VERSION_13,
|
||||
PG_VERSION_14,
|
||||
PG_VERSION_15,
|
||||
],
|
||||
|
||||
&VM_DB_TEST =>
|
||||
[
|
||||
PG_VERSION_10,
|
||||
PG_VERSION_11,
|
||||
PG_VERSION_12,
|
||||
PG_VERSION_13,
|
||||
@ -193,7 +192,6 @@ my $oyVm =
|
||||
|
||||
&VM_DB =>
|
||||
[
|
||||
PG_VERSION_10,
|
||||
PG_VERSION_11,
|
||||
PG_VERSION_12,
|
||||
PG_VERSION_13,
|
||||
@ -269,6 +267,7 @@ my $oyVm =
|
||||
[
|
||||
PG_VERSION_95,
|
||||
PG_VERSION_96,
|
||||
PG_VERSION_10,
|
||||
PG_VERSION_15,
|
||||
PG_VERSION_16,
|
||||
],
|
||||
|
@ -33,6 +33,7 @@ use pgBackRestTest::Env::Host::HostAzureTest;
|
||||
use pgBackRestTest::Env::Host::HostGcsTest;
|
||||
use pgBackRestTest::Env::Host::HostBaseTest;
|
||||
use pgBackRestTest::Env::Host::HostS3Test;
|
||||
use pgBackRestTest::Env::Host::HostSftpTest;
|
||||
use pgBackRestTest::Env::Manifest;
|
||||
use pgBackRestTest::Common::ContainerTest;
|
||||
use pgBackRestTest::Common::ExecuteTest;
|
||||
@ -92,6 +93,8 @@ use constant POSIX => STORAGE_P
|
||||
push @EXPORT, qw(POSIX);
|
||||
use constant S3 => 's3';
|
||||
push @EXPORT, qw(S3);
|
||||
use constant SFTP => 'sftp';
|
||||
push @EXPORT, qw(SFTP);
|
||||
|
||||
use constant CFGOPTVAL_RESTORE_TYPE_DEFAULT => 'default';
|
||||
push @EXPORT, qw(CFGOPTVAL_RESTORE_TYPE_DEFAULT);
|
||||
@ -161,7 +164,7 @@ sub new
|
||||
bless $self, $class;
|
||||
|
||||
# If repo is on local filesystem then set the repo-path locally
|
||||
if ($oParam->{bRepoLocal})
|
||||
if ($oParam->{bRepoLocal} || $oParam->{strBackupDestination} eq HOST_SFTP)
|
||||
{
|
||||
$self->{strRepoPath} = $self->testRunGet()->testPath() . "/$$oParam{strBackupDestination}/" . HOST_PATH_REPO;
|
||||
}
|
||||
@ -1265,6 +1268,35 @@ sub configCreate
|
||||
}
|
||||
}
|
||||
}
|
||||
elsif ($oParam->{strStorage} eq SFTP)
|
||||
{
|
||||
my $oHostDb1 = $oHostDbPrimary;
|
||||
my $oHostDb2 = $oHostDbStandby;
|
||||
|
||||
if ($self->nameTest(HOST_DB_STANDBY))
|
||||
{
|
||||
$oHostDb1 = $oHostDbStandby;
|
||||
$oHostDb2 = $oHostDbPrimary;
|
||||
}
|
||||
|
||||
# Set a flag so we know there's a bogus host
|
||||
$self->{bBogusHost} = true;
|
||||
|
||||
# Set a valid replica to a higher index to ensure skipping indexes does not make a difference
|
||||
$oParamHash{$strStanza}{"pg256-host"} = $oHostDb2->nameGet();
|
||||
$oParamHash{$strStanza}{"pg256-host-user"} = $oHostDb2->userGet();
|
||||
$oParamHash{$strStanza}{"pg256-host-cmd"} = $oHostDb2->backrestExe();
|
||||
$oParamHash{$strStanza}{"pg256-host-config"} = $oHostDb2->backrestConfig();
|
||||
$oParamHash{$strStanza}{"pg256-path"} = $oHostDb2->dbBasePath();
|
||||
|
||||
|
||||
# !!! is this needed???
|
||||
# Only test explicit ports on the backup server. This is so locally configured ports are also tested.
|
||||
if (!$self->synthetic())
|
||||
{
|
||||
$oParamHash{$strStanza}{"pg256-port"} = $oHostDb2->pgPort();
|
||||
}
|
||||
}
|
||||
|
||||
# If this is a database host
|
||||
if ($self->isHostDb())
|
||||
@ -1287,10 +1319,29 @@ sub configCreate
|
||||
# If the backup host is remote
|
||||
if (!$self->isHostBackup())
|
||||
{
|
||||
$oParamHash{&CFGDEF_SECTION_GLOBAL}{'repo1-host'} = $oHostBackup->nameGet();
|
||||
$oParamHash{&CFGDEF_SECTION_GLOBAL}{'repo1-host-user'} = $oHostBackup->userGet();
|
||||
|
||||
if ($oHostBackup->nameGet() eq HOST_SFTP)
|
||||
{
|
||||
$oParamHash{&CFGDEF_SECTION_GLOBAL}{'repo1-type'} = "sftp";
|
||||
$oParamHash{&CFGDEF_SECTION_GLOBAL}{'repo1-sftp-host'} = HOST_SFTP;
|
||||
$oParamHash{&CFGDEF_SECTION_GLOBAL}{'repo1-sftp-host-key-hash-type'} = "sha1";
|
||||
$oParamHash{&CFGDEF_SECTION_GLOBAL}{'repo1-sftp-host-user'} = TEST_USER;
|
||||
$oParamHash{&CFGDEF_SECTION_GLOBAL}{'repo1-sftp-private-key-file'} = testRunGet()->basePath() . SSH_PRIVATE_KEY;
|
||||
$oParamHash{&CFGDEF_SECTION_GLOBAL}{'repo1-sftp-public-key-file'} = testRunGet()->basePath() . SSH_PUBLIC_KEY;
|
||||
$oParamHash{&CFGDEF_SECTION_GLOBAL}{'repo1-path'} = $self->repoPath();
|
||||
|
||||
# At what count do we hit diminishing returns
|
||||
$oParamHash{&CFGDEF_SECTION_GLOBAL}{'process-max'} = 8;
|
||||
|
||||
$oParamHash{&CFGDEF_SECTION_GLOBAL . ':backup'}{'start-fast'} = 'y';
|
||||
}
|
||||
else
|
||||
{
|
||||
$oParamHash{&CFGDEF_SECTION_GLOBAL}{'repo1-host'} = $oHostBackup->nameGet();
|
||||
$oParamHash{&CFGDEF_SECTION_GLOBAL}{'repo1-host-cmd'} = $oHostBackup->backrestExe();
|
||||
$oParamHash{&CFGDEF_SECTION_GLOBAL}{'repo1-host-config'} = $oHostBackup->backrestConfig();
|
||||
}
|
||||
|
||||
if ($oParam->{bTls})
|
||||
{
|
||||
|
@ -43,6 +43,8 @@ use constant HOST_AZURE => 'azure';
|
||||
push @EXPORT, qw(HOST_AZURE);
|
||||
use constant HOST_S3 => 's3-server';
|
||||
push @EXPORT, qw(HOST_S3);
|
||||
use constant HOST_SFTP => 'sftp-srvr';
|
||||
push @EXPORT, qw(HOST_SFTP);
|
||||
|
||||
####################################################################################################################################
|
||||
# CA/cert/key constants
|
||||
@ -59,6 +61,16 @@ use constant HOST_SERVER_CA => HOST_CERT
|
||||
use constant HOST_SERVER_CERT => HOST_CERT_PATH . 'pgbackrest-test-server.crt';
|
||||
use constant HOST_SERVER_KEY => HOST_CERT_PATH . 'pgbackrest-test-server.key';
|
||||
|
||||
####################################################################################################################################
|
||||
# SFTP key constants
|
||||
####################################################################################################################################
|
||||
use constant SSH_KEY_PATH => '/test/certificate/ssh/';
|
||||
|
||||
use constant SSH_PRIVATE_KEY => SSH_KEY_PATH . 'id_rsa';
|
||||
push @EXPORT, qw(SSH_PRIVATE_KEY);
|
||||
use constant SSH_PUBLIC_KEY => SSH_KEY_PATH . 'id_rsa.pub';
|
||||
push @EXPORT, qw(SSH_PUBLIC_KEY);
|
||||
|
||||
####################################################################################################################################
|
||||
# new
|
||||
####################################################################################################################################
|
||||
|
72
test/lib/pgBackRestTest/Env/Host/HostSftpTest.pm
Normal file
72
test/lib/pgBackRestTest/Env/Host/HostSftpTest.pm
Normal file
@ -0,0 +1,72 @@
|
||||
####################################################################################################################################
|
||||
# SFTP Test Host
|
||||
####################################################################################################################################
|
||||
package pgBackRestTest::Env::Host::HostSftpTest;
|
||||
use parent 'pgBackRestTest::Common::HostTest';
|
||||
|
||||
####################################################################################################################################
|
||||
# Perl includes
|
||||
####################################################################################################################################
|
||||
use strict;
|
||||
use warnings FATAL => qw(all);
|
||||
use Carp qw(confess);
|
||||
|
||||
use Cwd qw(abs_path);
|
||||
use Exporter qw(import);
|
||||
our @EXPORT = qw();
|
||||
use File::Basename qw(dirname);
|
||||
use Storable qw(dclone);
|
||||
|
||||
use pgBackRestDoc::Common::Exception;
|
||||
use pgBackRestDoc::Common::Ini;
|
||||
use pgBackRestDoc::Common::Log;
|
||||
use pgBackRestDoc::ProjectInfo;
|
||||
|
||||
use pgBackRestTest::Common::ContainerTest;
|
||||
use pgBackRestTest::Common::ExecuteTest;
|
||||
use pgBackRestTest::Common::HostGroupTest;
|
||||
use pgBackRestTest::Common::RunTest;
|
||||
use pgBackRestTest::Common::StorageRepo;
|
||||
use pgBackRestTest::Common::Wait;
|
||||
use pgBackRestTest::Env::Host::HostBaseTest;
|
||||
use pgBackRestTest::Env::Manifest;
|
||||
|
||||
####################################################################################################################################
|
||||
# SFTP defaults
|
||||
####################################################################################################################################
|
||||
use constant HOST_SFTP_ACCOUNT => TEST_USER;
|
||||
push @EXPORT, qw(HOST_SFTP_ACCOUNT);
|
||||
use constant HOST_SFTP_HOSTKEY_HASH_TYPE => 'sha1';
|
||||
push @EXPORT, qw(HOST_SFTP_HOSTKEY_HASH_TYPE);
|
||||
|
||||
####################################################################################################################################
|
||||
# new
|
||||
####################################################################################################################################
|
||||
sub new
|
||||
{
|
||||
my $class = shift; # Class name
|
||||
|
||||
# Assign function parameters, defaults, and log debug info
|
||||
my
|
||||
(
|
||||
$strOperation,
|
||||
) =
|
||||
logDebugParam
|
||||
(
|
||||
__PACKAGE__ . '->new', \@_,
|
||||
);
|
||||
|
||||
# Create the host
|
||||
my $self = $class->SUPER::new(
|
||||
HOST_SFTP, 'test-' . testRunGet()->vmId() . '-' . HOST_SFTP, containerRepo() . ':' . testRunGet()->vm() . '-test', 'root');
|
||||
bless $self, $class;
|
||||
|
||||
# Return from function and log return values if any
|
||||
return logDebugReturn
|
||||
(
|
||||
$strOperation,
|
||||
{name => 'self', value => $self, trace => true}
|
||||
);
|
||||
}
|
||||
|
||||
1;
|
@ -70,6 +70,10 @@ sub setup
|
||||
{
|
||||
$oHostObject = new pgBackRestTest::Env::Host::HostGcsTest();
|
||||
}
|
||||
elsif ($oConfigParam->{strStorage} eq SFTP)
|
||||
{
|
||||
$oHostObject = new pgBackRestTest::Env::Host::HostSftpTest();
|
||||
}
|
||||
|
||||
# Get host group
|
||||
my $oHostGroup = hostGroupGet();
|
||||
@ -132,7 +136,7 @@ sub setup
|
||||
{
|
||||
$oHostGroup->hostAdd($oHostObject, {rstryHostName => ['pgbackrest-dev.s3.amazonaws.com', 's3.amazonaws.com']});
|
||||
}
|
||||
elsif ($oConfigParam->{strStorage} eq AZURE || $oConfigParam->{strStorage} eq GCS)
|
||||
elsif ($oConfigParam->{strStorage} eq AZURE || $oConfigParam->{strStorage} eq GCS || $oConfigParam->{strStorage} eq SFTP)
|
||||
{
|
||||
$oHostGroup->hostAdd($oHostObject);
|
||||
}
|
||||
@ -164,7 +168,8 @@ sub setup
|
||||
# If backup host is not defined set it to db-primary
|
||||
else
|
||||
{
|
||||
$oHostBackup = $strBackupDestination eq HOST_DB_PRIMARY ? $oHostDbPrimary : $oHostDbStandby;
|
||||
$oHostBackup = $strBackupDestination eq HOST_DB_PRIMARY || $strBackupDestination eq HOST_SFTP ? $oHostDbPrimary :
|
||||
$oHostDbStandby;
|
||||
}
|
||||
|
||||
storageRepoCommandSet(
|
||||
@ -172,7 +177,8 @@ sub setup
|
||||
' --config=' . $oHostBackup->backrestConfig() . ' --stanza=' . $self->stanza() . ' --log-level-console=off' .
|
||||
' --log-level-stderr=error' .
|
||||
($oConfigParam->{strStorage} ne POSIX ?
|
||||
" --no-repo1-storage-verify-tls --repo1-$oConfigParam->{strStorage}-" .
|
||||
($oConfigParam->{strStorage} ne SFTP ? " --no-repo1-storage-verify-tls" : '') .
|
||||
" --repo1-$oConfigParam->{strStorage}-" .
|
||||
($oConfigParam->{strStorage} eq GCS ? 'endpoint' : 'host') . "=" . $oHostObject->ipGet() : '') .
|
||||
($oConfigParam->{strStorage} eq GCS ? ':' . HOST_GCS_PORT : ''),
|
||||
$oConfigParam->{strStorage} eq POSIX ? STORAGE_POSIX : STORAGE_OBJECT);
|
||||
|
@ -54,13 +54,13 @@ sub run
|
||||
(
|
||||
{pg => '9.3', dst => 'backup', tls => 0, stg => AZURE, enc => 0, cmp => NONE, rt => 2, bnd => 0, bi => 0},
|
||||
{pg => '9.4', dst => 'db-standby', tls => 0, stg => POSIX, enc => 1, cmp => LZ4, rt => 1, bnd => 1, bi => 0},
|
||||
{pg => '9.5', dst => 'backup', tls => 1, stg => S3, enc => 0, cmp => BZ2, rt => 1, bnd => 0, bi => 1},
|
||||
{pg => '9.5', dst => 'backup', tls => 1, stg => GCS, enc => 0, cmp => BZ2, rt => 1, bnd => 0, bi => 1},
|
||||
{pg => '9.6', dst => 'backup', tls => 0, stg => POSIX, enc => 0, cmp => NONE, rt => 2, bnd => 1, bi => 1},
|
||||
{pg => '10', dst => 'db-standby', tls => 1, stg => GCS, enc => 1, cmp => GZ, rt => 2, bnd => 0, bi => 0},
|
||||
{pg => '11', dst => 'backup', tls => 1, stg => AZURE, enc => 0, cmp => ZST, rt => 2, bnd => 1, bi => 0},
|
||||
{pg => '10', dst => 'sftp-srvr', tls => 0, stg => SFTP, enc => 1, cmp => GZ, rt => 1, bnd => 1, bi => 0},
|
||||
{pg => '11', dst => 'backup', tls => 1, stg => AZURE, enc => 0, cmp => ZST, rt => 2, bnd => 0, bi => 0},
|
||||
{pg => '12', dst => 'backup', tls => 0, stg => S3, enc => 1, cmp => LZ4, rt => 1, bnd => 0, bi => 1},
|
||||
{pg => '13', dst => 'db-standby', tls => 1, stg => GCS, enc => 0, cmp => ZST, rt => 1, bnd => 1, bi => 1},
|
||||
{pg => '14', dst => 'backup', tls => 0, stg => POSIX, enc => 1, cmp => LZ4, rt => 2, bnd => 0, bi => 0},
|
||||
{pg => '14', dst => 'sftp-srvr', tls => 0, stg => SFTP, enc => 0, cmp => LZ4, rt => 1, bnd => 1, bi => 0},
|
||||
{pg => '15', dst => 'db-standby', tls => 0, stg => AZURE, enc => 0, cmp => NONE, rt => 2, bnd => 1, bi => 1},
|
||||
{pg => '16', dst => 'db-standby', tls => 0, stg => S3, enc => 1, cmp => NONE, rt => 1, bnd => 0, bi => 0},
|
||||
)
|
||||
|
@ -552,6 +552,7 @@ testBldUnit(TestBuild *const this)
|
||||
" lib_openssl,\n"
|
||||
" lib_lz4,\n"
|
||||
" lib_pq,\n"
|
||||
" lib_ssh2,\n"
|
||||
" lib_xml,\n"
|
||||
" lib_yaml,\n"
|
||||
" lib_z,\n"
|
||||
@ -661,6 +662,7 @@ testBldUnit(TestBuild *const this)
|
||||
strReplace(testC, STRDEF("{[C_TEST_GROUP]}"), groupName());
|
||||
strReplace(testC, STRDEF("{[C_TEST_GROUP_ID]}"), strNewFmt("%u", groupId()));
|
||||
strReplace(testC, STRDEF("{[C_TEST_USER]}"), userName());
|
||||
strReplace(testC, STRDEF("{[C_TEST_USER_LEN]}"), strNewFmt("%zu", strSize(userName())));
|
||||
strReplace(testC, STRDEF("{[C_TEST_USER_ID]}"), strNewFmt("%u", userId()));
|
||||
|
||||
// Test id
|
||||
|
719
test/src/common/harnessLibSsh2.c
Normal file
719
test/src/common/harnessLibSsh2.c
Normal file
@ -0,0 +1,719 @@
|
||||
/***********************************************************************************************************************************
|
||||
libssh2 Test Harness
|
||||
***********************************************************************************************************************************/
|
||||
#include "build.auto.h"
|
||||
|
||||
#ifdef HAVE_LIBSSH2
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "common/type/json.h"
|
||||
#include "common/type/string.h"
|
||||
#include "common/type/variantList.h"
|
||||
|
||||
#include "common/harnessLibSsh2.h"
|
||||
#include "common/harnessTest.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
libssh2 shim error prefix
|
||||
***********************************************************************************************************************************/
|
||||
#define LIBSSH2_ERROR_PREFIX "LIBSSH2 SHIM ERROR"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Script that defines how shim functions operate
|
||||
***********************************************************************************************************************************/
|
||||
HrnLibSsh2 hrnLibSsh2Script[1024];
|
||||
bool hrnLibSsh2ScriptDone = true;
|
||||
unsigned int hrnLibSsh2ScriptIdx;
|
||||
|
||||
// If there is a script failure change the behavior of cleanup functions to return immediately so the real error will be reported
|
||||
// rather than a bogus scripting error during cleanup
|
||||
bool hrnLibSsh2ScriptFail;
|
||||
char hrnLibSsh2ScriptError[4096];
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Set libssh2 script
|
||||
***********************************************************************************************************************************/
|
||||
void
|
||||
hrnLibSsh2ScriptSet(HrnLibSsh2 *hrnLibSsh2ScriptParam)
|
||||
{
|
||||
if (!hrnLibSsh2ScriptDone)
|
||||
THROW(AssertError, "previous libssh2 script has not yet completed");
|
||||
|
||||
if (hrnLibSsh2ScriptParam[0].function == NULL)
|
||||
THROW(AssertError, "libssh2 script must have entries");
|
||||
|
||||
// Copy records into local storage
|
||||
unsigned int copyIdx = 0;
|
||||
|
||||
while (hrnLibSsh2ScriptParam[copyIdx].function != NULL)
|
||||
{
|
||||
hrnLibSsh2Script[copyIdx] = hrnLibSsh2ScriptParam[copyIdx];
|
||||
copyIdx++;
|
||||
}
|
||||
|
||||
hrnLibSsh2Script[copyIdx].function = NULL;
|
||||
hrnLibSsh2ScriptDone = false;
|
||||
hrnLibSsh2ScriptIdx = 0;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Run libssh2 script
|
||||
***********************************************************************************************************************************/
|
||||
static HrnLibSsh2 *
|
||||
hrnLibSsh2ScriptRun(const char *const function, const VariantList *const param, const HrnLibSsh2 *const parent)
|
||||
{
|
||||
// If an error has already been thrown then throw the same error again
|
||||
if (hrnLibSsh2ScriptFail)
|
||||
THROW(AssertError, hrnLibSsh2ScriptError);
|
||||
|
||||
// Convert params to json for comparison and reporting
|
||||
String *paramStr = NULL;
|
||||
|
||||
if (param)
|
||||
{
|
||||
Variant *const varList = varNewVarLst(param);
|
||||
|
||||
paramStr = jsonFromVar(varList);
|
||||
varFree(varList);
|
||||
}
|
||||
else
|
||||
paramStr = strNew();
|
||||
|
||||
// Ensure script has not ended
|
||||
if (hrnLibSsh2ScriptDone)
|
||||
{
|
||||
snprintf(
|
||||
hrnLibSsh2ScriptError, sizeof(hrnLibSsh2ScriptError), "libssh2 script ended before %s (%s)", function,
|
||||
strZ(paramStr));
|
||||
|
||||
TEST_LOG_FMT(LIBSSH2_ERROR_PREFIX ": %s", hrnLibSsh2ScriptError);
|
||||
hrnLibSsh2ScriptFail = true;
|
||||
|
||||
THROW(AssertError, hrnLibSsh2ScriptError);
|
||||
}
|
||||
|
||||
// Get current script item
|
||||
HrnLibSsh2 *result = &hrnLibSsh2Script[hrnLibSsh2ScriptIdx];
|
||||
|
||||
// Check that expected function was called
|
||||
if (strcmp(result->function, function) != 0)
|
||||
{
|
||||
snprintf(
|
||||
hrnLibSsh2ScriptError, sizeof(hrnLibSsh2ScriptError),
|
||||
"libssh2 script [%u] expected function %s (%s) but got %s (%s)", hrnLibSsh2ScriptIdx, result->function,
|
||||
result->param == NULL ? "" : result->param, function, strZ(paramStr));
|
||||
|
||||
TEST_LOG_FMT(LIBSSH2_ERROR_PREFIX ": %s", hrnLibSsh2ScriptError);
|
||||
hrnLibSsh2ScriptFail = true;
|
||||
|
||||
THROW(AssertError, hrnLibSsh2ScriptError);
|
||||
}
|
||||
|
||||
// Check that parameters match
|
||||
if ((param != NULL && result->param == NULL) || (param == NULL && result->param != NULL) ||
|
||||
(param != NULL && result->param != NULL && !strEqZ(paramStr, result->param)))
|
||||
{
|
||||
snprintf(
|
||||
hrnLibSsh2ScriptError, sizeof(hrnLibSsh2ScriptError),
|
||||
"libssh2 script [%u] function '%s', expects param '%s' but got '%s'",
|
||||
hrnLibSsh2ScriptIdx, result->function, result->param ? result->param : "NULL", param ? strZ(paramStr) : "NULL");
|
||||
|
||||
TEST_LOG_FMT(LIBSSH2_ERROR_PREFIX ": %s", hrnLibSsh2ScriptError);
|
||||
hrnLibSsh2ScriptFail = true;
|
||||
|
||||
THROW(AssertError, hrnLibSsh2ScriptError);
|
||||
}
|
||||
|
||||
// Make sure the session matches with the parent as a sanity check
|
||||
if (parent != NULL && result->session != parent->session)
|
||||
{
|
||||
snprintf(
|
||||
hrnLibSsh2ScriptError, sizeof(hrnLibSsh2ScriptError),
|
||||
"libssh2 script [%u] function '%s', expects session '%u' but got '%u'",
|
||||
hrnLibSsh2ScriptIdx, result->function, result->session, parent->session);
|
||||
|
||||
TEST_LOG_FMT(LIBSSH2_ERROR_PREFIX ": %s", hrnLibSsh2ScriptError);
|
||||
hrnLibSsh2ScriptFail = true;
|
||||
|
||||
THROW(AssertError, hrnLibSsh2ScriptError);
|
||||
}
|
||||
|
||||
// Sleep if requested
|
||||
if (result->sleep > 0)
|
||||
sleepMSec(result->sleep);
|
||||
|
||||
hrnLibSsh2ScriptIdx++;
|
||||
|
||||
if (hrnLibSsh2Script[hrnLibSsh2ScriptIdx].function == NULL)
|
||||
hrnLibSsh2ScriptDone = true;
|
||||
|
||||
strFree(paramStr);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Shim for libssh2_init
|
||||
***********************************************************************************************************************************/
|
||||
int
|
||||
libssh2_init(int flags)
|
||||
{
|
||||
HrnLibSsh2 *hrnLibSsh2 = hrnLibSsh2ScriptRun(HRNLIBSSH2_INIT, varLstAdd(varLstNew(), varNewInt(flags)), NULL);
|
||||
|
||||
return hrnLibSsh2->resultInt;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Shim for libssh2_session_init
|
||||
***********************************************************************************************************************************/
|
||||
LIBSSH2_SESSION *
|
||||
libssh2_session_init_ex(
|
||||
LIBSSH2_ALLOC_FUNC((*myalloc)), LIBSSH2_FREE_FUNC((*myfree)), LIBSSH2_REALLOC_FUNC((*myrealloc)), void *abstract)
|
||||
{
|
||||
// All of these should always be the default NULL
|
||||
if (myalloc != NULL && myfree != NULL && myrealloc != NULL && abstract != NULL)
|
||||
{
|
||||
snprintf(
|
||||
hrnLibSsh2ScriptError, sizeof(hrnLibSsh2ScriptError),
|
||||
"libssh2 script function 'libssh2_session_init_ex', expects all params to be NULL");
|
||||
THROW(AssertError, hrnLibSsh2ScriptError);
|
||||
}
|
||||
|
||||
HrnLibSsh2 *hrnLibSsh2 = hrnLibSsh2ScriptRun(
|
||||
HRNLIBSSH2_SESSION_INIT_EX,
|
||||
varLstAdd(
|
||||
varLstAdd(
|
||||
varLstAdd(
|
||||
varLstAdd(
|
||||
varLstNew(), varNewStr(NULL)),
|
||||
varNewStr(NULL)),
|
||||
varNewStr(NULL)),
|
||||
varNewStr(NULL)),
|
||||
NULL);
|
||||
|
||||
return hrnLibSsh2->resultNull ? NULL : (LIBSSH2_SESSION *)hrnLibSsh2;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Shim for libssh2_session_set_blocking
|
||||
***********************************************************************************************************************************/
|
||||
void
|
||||
libssh2_session_set_blocking(LIBSSH2_SESSION *session, int blocking)
|
||||
{
|
||||
if (session == NULL)
|
||||
{
|
||||
snprintf(
|
||||
hrnLibSsh2ScriptError, sizeof(hrnLibSsh2ScriptError),
|
||||
"libssh2 script function 'libssh2_session_set_blocking', expects session to be not NULL");
|
||||
THROW(AssertError, hrnLibSsh2ScriptError);
|
||||
}
|
||||
|
||||
if (!(INT_MIN <= blocking && blocking <= INT_MAX))
|
||||
{
|
||||
snprintf(
|
||||
hrnLibSsh2ScriptError, sizeof(hrnLibSsh2ScriptError),
|
||||
"libssh2 script function 'libssh2_session_set_blocking', expects blocking to be an integer value");
|
||||
THROW(AssertError, hrnLibSsh2ScriptError);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Shim for libssh2_session_handshake
|
||||
***********************************************************************************************************************************/
|
||||
int
|
||||
libssh2_session_handshake(LIBSSH2_SESSION *session, libssh2_socket_t sock)
|
||||
{
|
||||
return hrnLibSsh2ScriptRun(
|
||||
HRNLIBSSH2_SESSION_HANDSHAKE, varLstAdd(varLstNew(), varNewInt(sock)), (HrnLibSsh2 *)session)->resultInt;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Shim for libssh2_hostkey_hash
|
||||
***********************************************************************************************************************************/
|
||||
const char *
|
||||
libssh2_hostkey_hash(LIBSSH2_SESSION *session, int hash_type)
|
||||
{
|
||||
HrnLibSsh2 *hrnLibSsh2 = NULL;
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
hrnLibSsh2 = hrnLibSsh2ScriptRun(
|
||||
HRNLIBSSH2_HOSTKEY_HASH, varLstAdd(varLstNew(), varNewInt(hash_type)), (HrnLibSsh2 *)session);
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
return hrnLibSsh2->resultNull ? NULL : (const char *)hrnLibSsh2->resultZ;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Shim for libssh2_userauth_publickey_fromfile_ex
|
||||
***********************************************************************************************************************************/
|
||||
int
|
||||
libssh2_userauth_publickey_fromfile_ex(
|
||||
LIBSSH2_SESSION *session, const char *username, unsigned int ousername_len, const char *publickey, const char *privatekey,
|
||||
const char *passphrase)
|
||||
{
|
||||
HrnLibSsh2 *hrnLibSsh2 = NULL;
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
hrnLibSsh2 = hrnLibSsh2ScriptRun(
|
||||
HRNLIBSSH2_USERAUTH_PUBLICKEY_FROMFILE_EX,
|
||||
varLstAdd(
|
||||
varLstAdd(
|
||||
varLstAdd(
|
||||
varLstAdd(
|
||||
varLstAdd(
|
||||
varLstNew(), varNewStrZ(username)),
|
||||
varNewUInt(ousername_len)),
|
||||
varNewStrZ(publickey)),
|
||||
varNewStrZ(privatekey)),
|
||||
varNewStrZ(passphrase)),
|
||||
(HrnLibSsh2 *)session);
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
return hrnLibSsh2->resultInt;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Shim for libssh2_sftp_init
|
||||
***********************************************************************************************************************************/
|
||||
LIBSSH2_SFTP *
|
||||
libssh2_sftp_init(LIBSSH2_SESSION *session)
|
||||
{
|
||||
HrnLibSsh2 *hrnLibSsh2 = hrnLibSsh2ScriptRun(HRNLIBSSH2_SFTP_INIT, NULL, (HrnLibSsh2 *)session);
|
||||
|
||||
return hrnLibSsh2->resultNull ? NULL : (LIBSSH2_SFTP *)hrnLibSsh2;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Shim for libssh2_sftp_close_handle
|
||||
***********************************************************************************************************************************/
|
||||
int
|
||||
libssh2_sftp_close_handle(LIBSSH2_SFTP_HANDLE *handle)
|
||||
{
|
||||
return hrnLibSsh2ScriptRun(HRNLIBSSH2_SFTP_CLOSE_HANDLE, NULL, (HrnLibSsh2 *)handle)->resultInt;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Shim for libssh2_sftp_shutdown
|
||||
***********************************************************************************************************************************/
|
||||
int
|
||||
libssh2_sftp_shutdown(LIBSSH2_SFTP *sftp)
|
||||
{
|
||||
return hrnLibSsh2ScriptRun(HRNLIBSSH2_SFTP_SHUTDOWN, NULL, (HrnLibSsh2 *)sftp)->resultInt;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Shim for libssh2_session_disconnect_ex
|
||||
***********************************************************************************************************************************/
|
||||
int
|
||||
libssh2_session_disconnect_ex(LIBSSH2_SESSION *session, int reason, const char *description, const char *lang)
|
||||
{
|
||||
HrnLibSsh2 *hrnLibSsh2 = NULL;
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
hrnLibSsh2 = hrnLibSsh2ScriptRun(
|
||||
HRNLIBSSH2_SESSION_DISCONNECT_EX,
|
||||
varLstAdd(
|
||||
varLstAdd(
|
||||
varLstAdd(
|
||||
varLstNew(), varNewInt(reason)),
|
||||
varNewStrZ(description)),
|
||||
varNewStrZ(lang)),
|
||||
(HrnLibSsh2 *)session);
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
return hrnLibSsh2->resultInt;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Shim for int libssh2_session_free
|
||||
***********************************************************************************************************************************/
|
||||
int
|
||||
libssh2_session_free(LIBSSH2_SESSION *session)
|
||||
{
|
||||
return hrnLibSsh2ScriptRun(HRNLIBSSH2_SESSION_FREE, NULL, (HrnLibSsh2 *)session)->resultInt;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Shim for libssh2_sftp_stat_ex
|
||||
***********************************************************************************************************************************/
|
||||
int
|
||||
libssh2_sftp_stat_ex(
|
||||
LIBSSH2_SFTP *sftp, const char *path, unsigned int path_len, int stat_type, LIBSSH2_SFTP_ATTRIBUTES *attrs)
|
||||
{
|
||||
// Avoid compiler complaining of unused param. Not passing to hrnLibSsh2ScriptRun, as parameter will vary depending on where
|
||||
// tests are being run.
|
||||
//
|
||||
// Could we utilize test.c/build.c to calculate/define this and other length params?
|
||||
(void)path_len;
|
||||
|
||||
HrnLibSsh2 *hrnLibSsh2 = NULL;
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
hrnLibSsh2 = hrnLibSsh2ScriptRun(
|
||||
HRNLIBSSH2_SFTP_STAT_EX,
|
||||
varLstAdd(
|
||||
varLstAdd(
|
||||
varLstNew(), varNewStrZ(path)),
|
||||
varNewInt(stat_type)),
|
||||
(HrnLibSsh2 *)sftp);
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
if (attrs == NULL)
|
||||
THROW(AssertError, "attrs is NULL");
|
||||
|
||||
attrs->flags = 0;
|
||||
attrs->flags |= (unsigned long)hrnLibSsh2->flags;
|
||||
|
||||
attrs->permissions = 0;
|
||||
attrs->permissions |= (unsigned long)hrnLibSsh2->attrPerms;
|
||||
|
||||
attrs->mtime = (unsigned long)hrnLibSsh2->mtime;
|
||||
attrs->uid = (unsigned long)hrnLibSsh2->uid;
|
||||
attrs->gid = (unsigned long)hrnLibSsh2->gid;
|
||||
attrs->filesize = hrnLibSsh2->filesize;
|
||||
|
||||
return hrnLibSsh2->resultInt;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Shim for libssh2_sftp_last_error
|
||||
***********************************************************************************************************************************/
|
||||
unsigned long
|
||||
libssh2_sftp_last_error(LIBSSH2_SFTP *sftp)
|
||||
{
|
||||
return (unsigned long)hrnLibSsh2ScriptRun(HRNLIBSSH2_SFTP_LAST_ERROR, NULL, (HrnLibSsh2 *)sftp)->resultUInt;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Shim for libssh2_sftp_symlink_ex
|
||||
***********************************************************************************************************************************/
|
||||
int
|
||||
libssh2_sftp_symlink_ex(
|
||||
LIBSSH2_SFTP *sftp, const char *path, unsigned int path_len, char *target, unsigned int target_len, int link_type)
|
||||
{
|
||||
// Avoid compiler complaining of unused param. Not passing to hrnLibSsh2ScriptRun, as parameter will vary depending on where
|
||||
// tests are being run.
|
||||
(void)path_len;
|
||||
(void)target_len;
|
||||
|
||||
HrnLibSsh2 *hrnLibSsh2 = NULL;
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
hrnLibSsh2 = hrnLibSsh2ScriptRun(
|
||||
HRNLIBSSH2_SFTP_SYMLINK_EX,
|
||||
varLstAdd(
|
||||
varLstAdd(
|
||||
varLstAdd(
|
||||
varLstNew(), varNewStrZ(path)),
|
||||
varNewStrZ(target)),
|
||||
varNewInt(link_type)),
|
||||
(HrnLibSsh2 *)sftp);
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
int rc = 0;
|
||||
|
||||
switch (link_type)
|
||||
{
|
||||
case LIBSSH2_SFTP_READLINK:
|
||||
case LIBSSH2_SFTP_REALPATH:
|
||||
if (hrnLibSsh2->symlinkExTarget != NULL)
|
||||
{
|
||||
if (strSize(hrnLibSsh2->symlinkExTarget) < PATH_MAX)
|
||||
strncpy(target, strZ(hrnLibSsh2->symlinkExTarget), strSize(hrnLibSsh2->symlinkExTarget));
|
||||
else
|
||||
THROW_FMT(AssertError, "symlinkExTarget too large for target buffer");
|
||||
}
|
||||
|
||||
rc = hrnLibSsh2->resultInt != 0 ? hrnLibSsh2->resultInt : (int)strSize(hrnLibSsh2->symlinkExTarget);
|
||||
break;
|
||||
|
||||
default:
|
||||
THROW_FMT(AssertError, "UNKNOWN link_type");
|
||||
break;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Shim for libssh2_sftp_open_ex
|
||||
***********************************************************************************************************************************/
|
||||
LIBSSH2_SFTP_HANDLE *
|
||||
libssh2_sftp_open_ex(
|
||||
LIBSSH2_SFTP *sftp, const char *filename, unsigned int filename_len, unsigned long flags, long mode, int open_type)
|
||||
{
|
||||
// To avoid compiler complaining of unused param. Not passing to hrnLibSsh2ScriptRun, as parameter will vary depending on where
|
||||
// tests are being run.
|
||||
(void)filename_len;
|
||||
|
||||
HrnLibSsh2 *hrnLibSsh2 = NULL;
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
hrnLibSsh2 = hrnLibSsh2ScriptRun(
|
||||
HRNLIBSSH2_SFTP_OPEN_EX,
|
||||
varLstAdd(
|
||||
varLstAdd(
|
||||
varLstAdd(
|
||||
varLstAdd(
|
||||
varLstNew(), varNewStrZ(filename)),
|
||||
varNewUInt64(flags)),
|
||||
varNewInt64(mode)),
|
||||
varNewInt(open_type)),
|
||||
(HrnLibSsh2 *)sftp);
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
return hrnLibSsh2->resultNull ? NULL : (LIBSSH2_SFTP_HANDLE *)hrnLibSsh2;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Shim for libssh2_sftp_readdir_ex
|
||||
***********************************************************************************************************************************/
|
||||
int
|
||||
libssh2_sftp_readdir_ex(
|
||||
LIBSSH2_SFTP_HANDLE *handle, char *buffer, size_t buffer_maxlen, char *longentry, size_t longentry_maxlen,
|
||||
LIBSSH2_SFTP_ATTRIBUTES *attrs)
|
||||
{
|
||||
if (attrs == NULL)
|
||||
THROW_FMT(AssertError, "attrs is NULL");
|
||||
|
||||
HrnLibSsh2 *hrnLibSsh2 = NULL;
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
hrnLibSsh2 = hrnLibSsh2ScriptRun(
|
||||
HRNLIBSSH2_SFTP_READDIR_EX,
|
||||
varLstAdd(
|
||||
varLstAdd(
|
||||
varLstAdd(
|
||||
varLstAdd(
|
||||
varLstNew(), varNewStrZ(buffer)),
|
||||
varNewUInt64(buffer_maxlen)),
|
||||
varNewStrZ(longentry)),
|
||||
varNewUInt64(longentry_maxlen)),
|
||||
(HrnLibSsh2 *)handle);
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
if (hrnLibSsh2->fileName != NULL)
|
||||
strncpy(buffer, strZ(hrnLibSsh2->fileName), buffer_maxlen);
|
||||
|
||||
return hrnLibSsh2->resultInt;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Shim for libssh2_session_last_errno
|
||||
***********************************************************************************************************************************/
|
||||
int
|
||||
libssh2_session_last_errno(LIBSSH2_SESSION *session)
|
||||
{
|
||||
return hrnLibSsh2ScriptRun(HRNLIBSSH2_SESSION_LAST_ERRNO, NULL, (HrnLibSsh2 *)session)->resultInt;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Shim for libssh2_sftp_fsync
|
||||
***********************************************************************************************************************************/
|
||||
int
|
||||
libssh2_sftp_fsync(LIBSSH2_SFTP_HANDLE *handle)
|
||||
{
|
||||
return hrnLibSsh2ScriptRun(HRNLIBSSH2_SFTP_FSYNC, NULL, (HrnLibSsh2 *)handle)->resultInt;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Shim for libssh2_sftp_mkdir_ex
|
||||
***********************************************************************************************************************************/
|
||||
int
|
||||
libssh2_sftp_mkdir_ex(LIBSSH2_SFTP *sftp, const char *path, unsigned int path_len, long mode)
|
||||
{
|
||||
// To avoid compiler complaining of unused param. Not passing to hrnLibSsh2ScriptRun, as parameter will vary depending on where
|
||||
// tests are being run.
|
||||
(void)path_len;
|
||||
|
||||
HrnLibSsh2 *hrnLibSsh2 = NULL;
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
hrnLibSsh2 = hrnLibSsh2ScriptRun(
|
||||
HRNLIBSSH2_SFTP_MKDIR_EX,
|
||||
varLstAdd(
|
||||
varLstAdd(
|
||||
varLstNew(), varNewStrZ(path)),
|
||||
varNewInt64(mode)),
|
||||
(HrnLibSsh2 *)sftp);
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
return hrnLibSsh2->resultInt;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Shim for libssh2_sftp_read
|
||||
***********************************************************************************************************************************/
|
||||
ssize_t
|
||||
libssh2_sftp_read(LIBSSH2_SFTP_HANDLE *handle, char *buffer, size_t buffer_maxlen)
|
||||
{
|
||||
// We don't pass buffer to hrnLibSsh2ScriptRun. The first call for each invocation passes buffer with random data, which is
|
||||
// an issue for sftpTest.c.
|
||||
HrnLibSsh2 *hrnLibSsh2 = NULL;
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
hrnLibSsh2 = hrnLibSsh2ScriptRun(
|
||||
HRNLIBSSH2_SFTP_READ,
|
||||
varLstAdd(
|
||||
varLstNew(), varNewUInt64(buffer_maxlen)),
|
||||
(HrnLibSsh2 *)handle);
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
// copy read into buffer
|
||||
if (hrnLibSsh2->readBuffer != NULL)
|
||||
strncpy(buffer, strZ(hrnLibSsh2->readBuffer), strSize(hrnLibSsh2->readBuffer));
|
||||
|
||||
// number of bytes populated
|
||||
return hrnLibSsh2->resultInt;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Shim for libssh2_sftp_rename_ex
|
||||
***********************************************************************************************************************************/
|
||||
int
|
||||
libssh2_sftp_rename_ex(
|
||||
LIBSSH2_SFTP *sftp, const char *source_filename, unsigned int source_filename_len, const char *dest_filename,
|
||||
unsigned int dest_filename_len, long flags)
|
||||
{
|
||||
// To avoid compiler complaining of unused param. Not passing to hrnLibSsh2ScriptRun, as parameter will vary depending on where
|
||||
// tests are being run.
|
||||
(void)source_filename_len;
|
||||
(void)dest_filename_len;
|
||||
|
||||
HrnLibSsh2 *hrnLibSsh2 = NULL;
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
hrnLibSsh2 = hrnLibSsh2ScriptRun(
|
||||
HRNLIBSSH2_SFTP_RENAME_EX,
|
||||
varLstAdd(
|
||||
varLstAdd(
|
||||
varLstAdd(
|
||||
varLstNew(), varNewStrZ(source_filename)),
|
||||
varNewStrZ(dest_filename)),
|
||||
varNewInt64(flags)),
|
||||
(HrnLibSsh2 *)sftp);
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
return hrnLibSsh2->resultInt;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Shim for libssh2_sftp_rmdir_ex
|
||||
***********************************************************************************************************************************/
|
||||
int
|
||||
libssh2_sftp_rmdir_ex(LIBSSH2_SFTP *sftp, const char *path, unsigned int path_len)
|
||||
{
|
||||
// Avoid compiler complaining of unused param. Not passing to hrnLibSsh2ScriptRun, as parameter will vary depending on where
|
||||
// tests are being run.
|
||||
(void)path_len;
|
||||
|
||||
HrnLibSsh2 *hrnLibSsh2 = NULL;
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
hrnLibSsh2 = hrnLibSsh2ScriptRun(
|
||||
HRNLIBSSH2_SFTP_RMDIR_EX,
|
||||
varLstAdd(
|
||||
varLstNew(), varNewStrZ(path)),
|
||||
(HrnLibSsh2 *)sftp);
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
return hrnLibSsh2->resultInt;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Shim for libssh2_sftp_seek64
|
||||
***********************************************************************************************************************************/
|
||||
void
|
||||
libssh2_sftp_seek64(LIBSSH2_SFTP_HANDLE *handle, libssh2_uint64_t offset)
|
||||
{
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
hrnLibSsh2ScriptRun(
|
||||
HRNLIBSSH2_SFTP_SEEK64,
|
||||
varLstAdd(
|
||||
varLstNew(), varNewUInt64(offset)),
|
||||
(HrnLibSsh2 *)handle);
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Shim for libssh2_sftp_unlink_ex
|
||||
***********************************************************************************************************************************/
|
||||
int
|
||||
libssh2_sftp_unlink_ex(LIBSSH2_SFTP *sftp, const char *filename, unsigned int filename_len)
|
||||
{
|
||||
// Avoid compiler complaining of unused param. Not passing to hrnLibSsh2ScriptRun, as parameter will vary depending on where
|
||||
// tests are being run.
|
||||
(void)filename_len;
|
||||
|
||||
HrnLibSsh2 *hrnLibSsh2 = NULL;
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
hrnLibSsh2 = hrnLibSsh2ScriptRun(
|
||||
HRNLIBSSH2_SFTP_UNLINK_EX,
|
||||
varLstAdd(
|
||||
varLstNew(), varNewStrZ(filename)),
|
||||
(HrnLibSsh2 *)sftp);
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
return hrnLibSsh2->resultInt;
|
||||
}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Shim for libssh2_sftp_write
|
||||
***********************************************************************************************************************************/
|
||||
ssize_t
|
||||
libssh2_sftp_write(LIBSSH2_SFTP_HANDLE *handle, const char *buffer, size_t count)
|
||||
{
|
||||
// We don't pass buffer to hrnLibSsh2ScriptRun. The first call for each invocation passes buffer with random data, which is
|
||||
// an issue for sftpTest.c.
|
||||
(void)buffer;
|
||||
|
||||
HrnLibSsh2 *hrnLibSsh2 = NULL;
|
||||
|
||||
MEM_CONTEXT_TEMP_BEGIN()
|
||||
{
|
||||
hrnLibSsh2 = hrnLibSsh2ScriptRun(
|
||||
HRNLIBSSH2_SFTP_WRITE,
|
||||
varLstAdd(
|
||||
varLstNew(), varNewUInt64(count)),
|
||||
(HrnLibSsh2 *)handle);
|
||||
}
|
||||
MEM_CONTEXT_TEMP_END();
|
||||
|
||||
// Return number of bytes written
|
||||
return hrnLibSsh2->resultInt;
|
||||
}
|
||||
|
||||
#endif // HAVE_LIBSSH2
|
122
test/src/common/harnessLibSsh2.h
Normal file
122
test/src/common/harnessLibSsh2.h
Normal file
@ -0,0 +1,122 @@
|
||||
/***********************************************************************************************************************************
|
||||
libssh2 Test Harness
|
||||
|
||||
Scripted testing for libssh2 so exact results can be returned for unit testing. See sftp unit tests for usage examples.
|
||||
***********************************************************************************************************************************/
|
||||
#ifndef TEST_COMMON_HARNESS_LIBSSH2_H
|
||||
#define TEST_COMMON_HARNESS_LIBSSH2_H
|
||||
|
||||
#ifdef HAVE_LIBSSH2
|
||||
|
||||
#ifndef HARNESS_LIBSSH2_REAL
|
||||
|
||||
#include <libssh2.h>
|
||||
#include <libssh2_sftp.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "common/macro.h"
|
||||
#include "common/time.h"
|
||||
#include "version.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
libssh2 authorization constants
|
||||
***********************************************************************************************************************************/
|
||||
#define KEYPRIV STRDEF("/home/" TEST_USER "/.ssh/id_rsa")
|
||||
#define KEYPUB STRDEF("/home/" TEST_USER "/.ssh/id_rsa.pub")
|
||||
#define KEYPRIV_CSTR "/home/" TEST_USER "/.ssh/id_rsa"
|
||||
#define KEYPUB_CSTR "/home/" TEST_USER "/.ssh/id_rsa.pub"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Function constants
|
||||
***********************************************************************************************************************************/
|
||||
#define HRNLIBSSH2_HOSTKEY_HASH "libssh2_hostkey_hash"
|
||||
#define HRNLIBSSH2_INIT "libssh2_init"
|
||||
#define HRNLIBSSH2_SESSION_DISCONNECT_EX "libssh2_session_disconnect_ex"
|
||||
#define HRNLIBSSH2_SESSION_FREE "libssh2_session_free"
|
||||
#define HRNLIBSSH2_SESSION_HANDSHAKE "libssh2_session_handshake"
|
||||
#define HRNLIBSSH2_SESSION_INIT_EX "libssh2_session_init_ex"
|
||||
#define HRNLIBSSH2_SESSION_LAST_ERRNO "libssh2_session_last_errno"
|
||||
#define HRNLIBSSH2_SESSION_LAST_ERROR "libssh2_session_last_error"
|
||||
#define HRNLIBSSH2_SESSION_SET_BLOCKING "libssh2_session_set_blocking"
|
||||
#define HRNLIBSSH2_SFTP_CLOSE_HANDLE "libssh2_sftp_close_handle"
|
||||
#define HRNLIBSSH2_SFTP_FSYNC "libssh2_sftp_fsync"
|
||||
#define HRNLIBSSH2_SFTP_INIT "libssh2_sftp_init"
|
||||
#define HRNLIBSSH2_SFTP_LAST_ERROR "libssh2_sftp_last_error"
|
||||
#define HRNLIBSSH2_SFTP_MKDIR_EX "libssh2_sftp_mkdir_ex"
|
||||
#define HRNLIBSSH2_SFTP_OPEN_EX "libssh2_sftp_open_ex"
|
||||
#define HRNLIBSSH2_SFTP_READ "libssh2_sftp_read"
|
||||
#define HRNLIBSSH2_SFTP_READDIR_EX "libssh2_sftp_readdir_ex"
|
||||
#define HRNLIBSSH2_SFTP_RENAME_EX "libssh2_sftp_rename_ex"
|
||||
#define HRNLIBSSH2_SFTP_RMDIR_EX "libssh2_sftp_rmdir_ex"
|
||||
#define HRNLIBSSH2_SFTP_SEEK64 "libssh2_sftp_seek64"
|
||||
#define HRNLIBSSH2_SFTP_SHUTDOWN "libssh2_sftp_shutdown"
|
||||
#define HRNLIBSSH2_SFTP_STAT_EX "libssh2_sftp_stat_ex"
|
||||
#define HRNLIBSSH2_SFTP_SYMLINK_EX "libssh2_sftp_symlink_ex"
|
||||
#define HRNLIBSSH2_SFTP_UNLINK_EX "libssh2_sftp_unlink_ex"
|
||||
#define HRNLIBSSH2_SFTP_WRITE "libssh2_sftp_write"
|
||||
#define HRNLIBSSH2_USERAUTH_PUBLICKEY_FROMFILE_EX "libssh2_userauth_publickey_fromfile_ex"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Macros for defining groups of functions that implement commands
|
||||
***********************************************************************************************************************************/
|
||||
// Set of functions mimicking libssh2 inititialization and authorization
|
||||
#define HRNLIBSSH2_MACRO_STARTUP() \
|
||||
{.function = HRNLIBSSH2_INIT, .param = "[0]", .resultInt = 0}, \
|
||||
{.function = HRNLIBSSH2_SESSION_INIT_EX, .param = "[null,null,null,null]"}, \
|
||||
{.function = HRNLIBSSH2_SESSION_HANDSHAKE, .param = HANDSHAKE_PARAM, .resultInt = 0}, \
|
||||
{.function = HRNLIBSSH2_HOSTKEY_HASH, .param = "[2]", .resultZ = "12345678910123456789"}, \
|
||||
{.function = HRNLIBSSH2_USERAUTH_PUBLICKEY_FROMFILE_EX, \
|
||||
.param = "[\"" TEST_USER "\"," TEST_USER_LEN ",\"" KEYPUB_CSTR "\",\"" KEYPRIV_CSTR "\",null]", \
|
||||
.resultInt = 0}, \
|
||||
{.function = HRNLIBSSH2_SFTP_INIT}
|
||||
|
||||
// Set of functions mimicking libssh2 shutdown and disconnect
|
||||
#define HRNLIBSSH2_MACRO_SHUTDOWN() \
|
||||
{.function = HRNLIBSSH2_SFTP_SHUTDOWN, .resultInt = 0}, \
|
||||
{.function = HRNLIBSSH2_SESSION_DISCONNECT_EX, .param ="[11,\"pgbackrest instance shutdown\",\"\"]", .resultInt = 0}, \
|
||||
{.function = HRNLIBSSH2_SESSION_FREE, .resultInt = 0}, \
|
||||
{.function = NULL} \
|
||||
|
||||
// older systems do not support LIBSSH2_HOSTKEY_HASH_SHA256
|
||||
#ifdef LIBSSH2_HOSTKEY_HASH_SHA256
|
||||
#define HOSTKEY_HASH_ENTRY() \
|
||||
{.function = HRNLIBSSH2_HOSTKEY_HASH, .param = "[3]", .resultZ = "12345678910123456789"}
|
||||
#else
|
||||
#define HOSTKEY_HASH_ENTRY() \
|
||||
{.function = HRNLIBSSH2_HOSTKEY_HASH, .param = "[2]", .resultZ = "12345678910123456789"}
|
||||
#endif
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Structure for scripting libssh2 responses
|
||||
***********************************************************************************************************************************/
|
||||
typedef struct HrnLibSsh2
|
||||
{
|
||||
unsigned int session; // Session number when multiple sessions are run concurrently
|
||||
const char *function; // Function call expected
|
||||
const char *param; // Params expected by the function for verification
|
||||
int resultInt; // Int result value
|
||||
uint64_t resultUInt; // UInt result value
|
||||
const char *resultZ; // Zero-terminated result value
|
||||
bool resultNull; // Return null from function that normally returns a struct ptr
|
||||
uint64_t flags; // libssh2 flags
|
||||
uint64_t attrPerms; // libssh2 attr perms
|
||||
uint64_t atime, mtime; // libssh2 timestamps
|
||||
uint64_t uid, gid; // libssh2 uid/gid
|
||||
uint64_t filesize; // libssh2 filesize
|
||||
uint64_t offset; // libssh2 seek offset
|
||||
const String *symlinkExTarget; // libssh2_sftp_symlink_ex target
|
||||
const String *fileName; // libssh2_readdir* libssh2_stat* filename
|
||||
const String *readBuffer; // what to copy into read buffer
|
||||
TimeMSec sleep; // Sleep specified milliseconds before returning from function
|
||||
} HrnLibSsh2;
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Functions
|
||||
***********************************************************************************************************************************/
|
||||
void hrnLibSsh2ScriptSet(HrnLibSsh2 *hrnLibSsh2ScriptParam);
|
||||
|
||||
#endif // HARNESS_LIBSSH2_REAL
|
||||
|
||||
#endif // HAVE_LIBSSH2
|
||||
|
||||
#endif // TEST_COMMON_HARNESS_LIBSSH2_H
|
75
test/src/common/harnessSocket.c
Normal file
75
test/src/common/harnessSocket.c
Normal file
@ -0,0 +1,75 @@
|
||||
/***********************************************************************************************************************************
|
||||
Harness for Io Testing
|
||||
***********************************************************************************************************************************/
|
||||
#include "build.auto.h"
|
||||
|
||||
#include "common/harnessConfig.h"
|
||||
#include "common/harnessDebug.h"
|
||||
#include "common/harnessSocket.h"
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Include shimmed C modules
|
||||
***********************************************************************************************************************************/
|
||||
{[SHIM_MODULE]}
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Shim install state
|
||||
***********************************************************************************************************************************/
|
||||
static struct
|
||||
{
|
||||
// Local process shims
|
||||
bool localShimSckClientOpen;
|
||||
} hrnIoStatic;
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Shim sckClientOpen()
|
||||
***********************************************************************************************************************************/
|
||||
IoSession *
|
||||
sckClientOpen(THIS_VOID)
|
||||
{
|
||||
THIS(SocketClient);
|
||||
|
||||
FUNCTION_HARNESS_BEGIN();
|
||||
FUNCTION_HARNESS_PARAM(SOCKET_CLIENT, this);
|
||||
FUNCTION_HARNESS_END();
|
||||
|
||||
ASSERT(this != NULL);
|
||||
|
||||
IoSession *result = NULL;
|
||||
|
||||
// When shim is installed create IoSession with a known fd
|
||||
if (hrnIoStatic.localShimSckClientOpen)
|
||||
{
|
||||
result = sckSessionNew(ioSessionRoleClient, HRN_SCK_FILE_DESCRIPTOR, this->host, this->port, this->timeoutSession);
|
||||
|
||||
// Remove the callback so we will not try to close the fake descriptor
|
||||
memContextCallbackClear(objMemContext(((IoSessionPub *)result)->driver));
|
||||
}
|
||||
// Else call normal function
|
||||
else
|
||||
result = sckClientOpen_SHIMMED(this);
|
||||
|
||||
FUNCTION_HARNESS_RETURN(IO_SESSION, result);
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
void
|
||||
hrnSckClientOpenShimInstall(void)
|
||||
{
|
||||
FUNCTION_HARNESS_VOID();
|
||||
|
||||
hrnIoStatic.localShimSckClientOpen = true;
|
||||
|
||||
FUNCTION_HARNESS_RETURN_VOID();
|
||||
}
|
||||
|
||||
/**********************************************************************************************************************************/
|
||||
void
|
||||
hrnSckClientOpenShimUninstall(void)
|
||||
{
|
||||
FUNCTION_HARNESS_VOID();
|
||||
|
||||
hrnIoStatic.localShimSckClientOpen = false;
|
||||
|
||||
FUNCTION_HARNESS_RETURN_VOID();
|
||||
}
|
16
test/src/common/harnessSocket.h
Normal file
16
test/src/common/harnessSocket.h
Normal file
@ -0,0 +1,16 @@
|
||||
/***********************************************************************************************************************************
|
||||
Harness for Io Testing
|
||||
***********************************************************************************************************************************/
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Constants
|
||||
***********************************************************************************************************************************/
|
||||
// Arbitrary value for the shimmed socket file descriptor
|
||||
#define HRN_SCK_FILE_DESCRIPTOR 1163581
|
||||
|
||||
/***********************************************************************************************************************************
|
||||
Functions
|
||||
***********************************************************************************************************************************/
|
||||
// Install/uninstall the shims for testing
|
||||
void hrnSckClientOpenShimInstall(void);
|
||||
void hrnSckClientOpenShimUninstall(void);
|
@ -215,8 +215,8 @@ testRun(void)
|
||||
" --set backup set to restore [default=latest]\n"
|
||||
" --tablespace-map restore a tablespace into the specified\n"
|
||||
" directory\n"
|
||||
" --tablespace-map-all restore all tablespaces into the specified\n"
|
||||
" directory\n"
|
||||
" --tablespace-map-all restore all tablespaces into the\n"
|
||||
" specified directory\n"
|
||||
" --target recovery target\n"
|
||||
" --target-action action to take when recovery target is\n"
|
||||
" reached [default=pause]\n"
|
||||
@ -235,18 +235,19 @@ testRun(void)
|
||||
" --compress-level-network network compression level [default=3]\n"
|
||||
" --config pgBackRest configuration file\n"
|
||||
" [default=/etc/pgbackrest/pgbackrest.conf]\n"
|
||||
" --config-include-path path to additional pgBackRest configuration\n"
|
||||
" files [default=/etc/pgbackrest/conf.d]\n"
|
||||
" --config-path base path of pgBackRest configuration files\n"
|
||||
" [default=/etc/pgbackrest]\n"
|
||||
" --config-include-path path to additional pgBackRest\n"
|
||||
" configuration files\n"
|
||||
" [default=/etc/pgbackrest/conf.d]\n"
|
||||
" --config-path base path of pgBackRest configuration\n"
|
||||
" files [default=/etc/pgbackrest]\n"
|
||||
" --delta restore or backup using checksums\n"
|
||||
" [default=n]\n"
|
||||
" --io-timeout I/O timeout [default=60]\n"
|
||||
" --lock-path path where lock files are stored\n"
|
||||
" [default=/tmp/pgbackrest]\n"
|
||||
" --neutral-umask use a neutral umask [default=y]\n"
|
||||
" --process-max max processes to use for compress/transfer\n"
|
||||
" [default=1]\n"
|
||||
" --process-max max processes to use for\n"
|
||||
" compress/transfer [default=1]\n"
|
||||
" --protocol-timeout protocol timeout [default=1830]\n"
|
||||
" --sck-keep-alive keep-alive enable [default=y]\n"
|
||||
" --stanza defines the stanza\n"
|
||||
@ -300,9 +301,10 @@ testRun(void)
|
||||
" path [default=/etc/pgbackrest]\n"
|
||||
" --repo-host-key-file repository host key file\n"
|
||||
" --repo-host-port repository host port when repo-host is set\n"
|
||||
" --repo-host-type repository host protocol type [default=ssh]\n"
|
||||
" --repo-host-user repository host user when repo-host is set\n"
|
||||
" [default=pgbackrest]\n"
|
||||
" --repo-host-type repository host protocol type\n"
|
||||
" [default=ssh]\n"
|
||||
" --repo-host-user repository host user when repo-host is\n"
|
||||
" set [default=pgbackrest]\n"
|
||||
" --repo-path path where backups and archive are stored\n"
|
||||
" [default=/var/lib/pgbackrest]\n"
|
||||
" --repo-s3-bucket S3 repository bucket\n"
|
||||
@ -315,6 +317,14 @@ testRun(void)
|
||||
" --repo-s3-role S3 repository role\n"
|
||||
" --repo-s3-token S3 repository security token\n"
|
||||
" --repo-s3-uri-style S3 URI Style [default=host]\n"
|
||||
" --repo-sftp-host SFTP repository host\n"
|
||||
" --repo-sftp-host-fingerprint SFTP repository host fingerprint\n"
|
||||
" --repo-sftp-host-key-hash-type SFTP repository host key hash type\n"
|
||||
" --repo-sftp-host-port SFTP repository host port [default=22]\n"
|
||||
" --repo-sftp-host-user SFTP repository host user\n"
|
||||
" --repo-sftp-private-key-file SFTP private key file\n"
|
||||
" --repo-sftp-private-key-passphrase SFTP private key passphrase\n"
|
||||
" --repo-sftp-public-key-file SFTP public key file\n"
|
||||
" --repo-storage-ca-file repository storage CA file\n"
|
||||
" --repo-storage-ca-path repository storage CA path\n"
|
||||
" --repo-storage-host repository storage host\n"
|
||||
|
@ -117,6 +117,25 @@ testRun(void)
|
||||
hrnCfgLoadP(cfgCmdInfo, argList), OptionInvalidValueError,
|
||||
"local repo1 and repo2 paths are both '/var/lib/pgbackrest' but must be different");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("local default repo paths for sftp repo type must be different");
|
||||
|
||||
argList = strLstNew();
|
||||
hrnCfgArgRawZ(argList, cfgOptRepo, "2");
|
||||
hrnCfgArgKeyRawStrId(argList, cfgOptRepoType, 1, STORAGE_SFTP_TYPE);
|
||||
hrnCfgArgKeyRawStrId(argList, cfgOptRepoType, 2, STORAGE_SFTP_TYPE);
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptRepoSftpHostUser, 1, "user1");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptRepoSftpHostUser, 2, "user2");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptRepoSftpHost, 1, "host1");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptRepoSftpHost, 2, "host2");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptRepoSftpHostKeyHashType, 1, "sha1");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptRepoSftpHostKeyHashType, 2, "md5");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptRepoSftpPrivateKeyFile, 1, "/keyfile1");
|
||||
hrnCfgArgKeyRawZ(argList, cfgOptRepoSftpPrivateKeyFile, 2, "/keyfile2");
|
||||
TEST_ERROR(
|
||||
hrnCfgLoadP(cfgCmdInfo, argList), OptionInvalidValueError,
|
||||
"local repo1 and repo2 paths are both '/var/lib/pgbackrest' but must be different");
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
TEST_TITLE("local repo paths same but types different");
|
||||
|
||||
|
4565
test/src/module/storage/sftpTest.c
Normal file
4565
test/src/module/storage/sftpTest.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -142,6 +142,7 @@ testRun(void)
|
||||
strReplace(testC, STRDEF("{[C_TEST_USER]}"), STRDEF(TEST_USER));
|
||||
strReplace(testC, STRDEF("{[C_TEST_USER_ID]}"), STRDEF(TEST_USER_ID_Z));
|
||||
strReplace(testC, STRDEF("{[C_TEST_USER_ID_Z]}"), STRDEF("\"" TEST_USER_ID_Z "\""));
|
||||
strReplace(testC, STRDEF("{[C_TEST_USER_LEN]}"), strNewFmt("%zu", sizeof(TEST_USER) - 1));
|
||||
|
||||
// Test definition
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
@ -314,6 +315,7 @@ testRun(void)
|
||||
" lib_openssl,\n"
|
||||
" lib_lz4,\n"
|
||||
" lib_pq,\n"
|
||||
" lib_ssh2,\n"
|
||||
" lib_xml,\n"
|
||||
" lib_yaml,\n"
|
||||
" lib_z,\n"
|
||||
@ -429,6 +431,7 @@ testRun(void)
|
||||
" lib_openssl,\n"
|
||||
" lib_lz4,\n"
|
||||
" lib_pq,\n"
|
||||
" lib_ssh2,\n"
|
||||
" lib_xml,\n"
|
||||
" lib_yaml,\n"
|
||||
" lib_z,\n"
|
||||
@ -639,6 +642,7 @@ testRun(void)
|
||||
" lib_openssl,\n"
|
||||
" lib_lz4,\n"
|
||||
" lib_pq,\n"
|
||||
" lib_ssh2,\n"
|
||||
" lib_xml,\n"
|
||||
" lib_yaml,\n"
|
||||
" lib_z,\n"
|
||||
@ -809,6 +813,7 @@ testRun(void)
|
||||
" lib_openssl,\n"
|
||||
" lib_lz4,\n"
|
||||
" lib_pq,\n"
|
||||
" lib_ssh2,\n"
|
||||
" lib_xml,\n"
|
||||
" lib_yaml,\n"
|
||||
" lib_z,\n"
|
||||
|
@ -77,6 +77,7 @@ STRING_EXTERN(HRN_PATH_STR, HRN_PATH);
|
||||
#define TEST_USER "{[C_TEST_USER]}"
|
||||
#define TEST_USER_ID {[C_TEST_USER_ID]}
|
||||
#define TEST_USER_ID_Z "{[C_TEST_USER_ID]}"
|
||||
#define TEST_USER_LEN "{[C_TEST_USER_LEN]}"
|
||||
|
||||
#ifdef HRN_FEATURE_STRING
|
||||
STRING_EXTERN(TEST_USER_STR, TEST_USER);
|
||||
|
Reference in New Issue
Block a user