mirror of
https://git.libssh.org/projects/libssh.git
synced 2025-07-28 01:41:48 +03:00
Add tutorial for the sftp aio API
Signed-off-by: Eshan Kelkar <eshankelkar@galorithm.com> Reviewed-by: Sahana Prasad <sahana@redhat.com> Reviewed-by: Jakub Jelen <jjelen@redhat.com>
This commit is contained in:
committed by
Sahana Prasad
parent
12f28a519b
commit
d1960cb9a2
@ -46,6 +46,8 @@ Table of contents:
|
|||||||
|
|
||||||
@subpage libssh_tutor_pkcs11
|
@subpage libssh_tutor_pkcs11
|
||||||
|
|
||||||
|
@subpage libssh_tutor_sftp_aio
|
||||||
|
|
||||||
@subpage libssh_tutor_todo
|
@subpage libssh_tutor_todo
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
575
doc/sftp_aio.dox
Normal file
575
doc/sftp_aio.dox
Normal file
@ -0,0 +1,575 @@
|
|||||||
|
/**
|
||||||
|
|
||||||
|
@page libssh_tutor_sftp_aio Chapter 10: The SFTP asynchronous I/O
|
||||||
|
|
||||||
|
@section sftp_aio_api The SFTP asynchronous I/O
|
||||||
|
|
||||||
|
NOTE : Please read @ref libssh_tutor_sftp before reading this page. The
|
||||||
|
synchronous sftp_read() and sftp_write() have been described there.
|
||||||
|
|
||||||
|
SFTP AIO stands for "SFTP Asynchronous Input/Output". This API contains
|
||||||
|
functions which perform async read/write operations on remote files.
|
||||||
|
|
||||||
|
File transfers performed using the asynchronous sftp aio API can be
|
||||||
|
significantly faster than the file transfers performed using the synchronous
|
||||||
|
sftp read/write API (see sftp_read() and sftp_write()).
|
||||||
|
|
||||||
|
The sftp aio API functions are divided into two categories :
|
||||||
|
- sftp_aio_begin_*() [see sftp_aio_begin_read(), sftp_aio_begin_write()]:
|
||||||
|
These functions send a request for an i/o operation to the server and
|
||||||
|
provide the caller an sftp aio handle corresponding to the sent request.
|
||||||
|
|
||||||
|
- sftp_aio_wait_*() [see sftp_aio_wait_read(), sftp_aio_wait_write()]:
|
||||||
|
These functions wait for the server response corresponding to a previously
|
||||||
|
issued request. Which request ? the request corresponding to the sftp aio
|
||||||
|
handle supplied by the caller to these functions.
|
||||||
|
|
||||||
|
Conceptually, you can think of the sftp aio handle as a request identifier.
|
||||||
|
|
||||||
|
Technically, the sftp_aio_begin_*() functions dynamically allocate memory to
|
||||||
|
store information about the i/o request they send and provide the caller a
|
||||||
|
handle to this memory, we call this handle an sftp aio handle.
|
||||||
|
|
||||||
|
sftp_aio_wait_*() functions use the information stored in that memory (handled
|
||||||
|
by the caller supplied sftp aio handle) to identify a request, and then they
|
||||||
|
wait for that request's response. These functions also release the memory
|
||||||
|
handled by the caller supplied sftp aio handle (except when they return
|
||||||
|
SSH_AGAIN).
|
||||||
|
|
||||||
|
sftp_aio_free() can also be used to release the memory handled by an sftp aio
|
||||||
|
handle but unlike the sftp_aio_wait_*() functions, it doesn't wait for a
|
||||||
|
response. This should be used to release the memory corresponding to an sftp
|
||||||
|
aio handle when some failure occurs. An example has been provided at the
|
||||||
|
end of this page to show the usage of sftp_aio_free().
|
||||||
|
|
||||||
|
To begin with, this tutorial will provide basic examples that describe the
|
||||||
|
usage of sftp aio API to perform a single read/write operation.
|
||||||
|
|
||||||
|
The later sections describe the usage of the sftp aio API to obtain faster file
|
||||||
|
transfers as compared to the transfers performed using the synchronous sftp
|
||||||
|
read/write API.
|
||||||
|
|
||||||
|
On encountering an error, the sftp aio API functions set the sftp and ssh
|
||||||
|
errors just like any other libssh sftp API function. These errors can be
|
||||||
|
obtained using sftp_get_error(), ssh_get_error() and ssh_get_error_code().
|
||||||
|
The code examples provided on this page ignore error handling for the sake of
|
||||||
|
brevity.
|
||||||
|
|
||||||
|
@subsection sftp_aio_read Using the sftp aio API for reading (a basic example)
|
||||||
|
|
||||||
|
For performing an async read operation on a sftp file (see sftp_open()),
|
||||||
|
the first step is to call sftp_aio_begin_read() to send a read request to the
|
||||||
|
server. The caller is provided an sftp aio handle corresponding to the sent
|
||||||
|
read request.
|
||||||
|
|
||||||
|
The second step is to pass a pointer to this aio handle to
|
||||||
|
sftp_aio_wait_read(), this function waits for the server response which
|
||||||
|
indicates the success/failure of the read request. On success, the response
|
||||||
|
indicates EOF or contains the data read from the sftp file.
|
||||||
|
|
||||||
|
The following code example shows how a read operation can be performed
|
||||||
|
on an sftp file using the sftp aio API.
|
||||||
|
|
||||||
|
@code
|
||||||
|
ssize_t read_chunk(sftp_file file, void *buf, size_t to_read)
|
||||||
|
{
|
||||||
|
ssize_t bytes_read;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
// Variable to store an sftp aio handle
|
||||||
|
sftp_aio aio = NULL;
|
||||||
|
|
||||||
|
// Send a read request to the sftp server
|
||||||
|
rc = sftp_aio_begin_read(file, to_read, &aio);
|
||||||
|
if (rc == SSH_ERROR) {
|
||||||
|
// handle error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the response of the read request corresponding to the
|
||||||
|
// sftp aio handle stored in the aio variable.
|
||||||
|
bytes_read = sftp_aio_wait_read(&aio, buf, to_read);
|
||||||
|
if (bytes_read == SSH_ERROR) {
|
||||||
|
// handle error
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes_read;
|
||||||
|
}
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
@subsection sftp_aio_write Using the sftp aio API for writing (a basic example)
|
||||||
|
|
||||||
|
For performing an async write operation on a sftp file (see sftp_open()),
|
||||||
|
the first step is to call sftp_aio_begin_write() to send a write request to
|
||||||
|
the server. The caller is provided an sftp aio handle corresponding to the
|
||||||
|
sent write request.
|
||||||
|
|
||||||
|
The second step is to pass a pointer to this aio handle to
|
||||||
|
sftp_aio_wait_write(), this function waits for the server response which
|
||||||
|
indicates the success/failure of the write request.
|
||||||
|
|
||||||
|
The following code example shows how a write operation can be performed on an
|
||||||
|
sftp file using the sftp aio API.
|
||||||
|
|
||||||
|
@code
|
||||||
|
ssize_t write_chunk(sftp_file file, void *buf, size_t to_write)
|
||||||
|
{
|
||||||
|
ssize_t bytes_written;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
// Variable to store an sftp aio handle
|
||||||
|
sftp_aio aio = NULL;
|
||||||
|
|
||||||
|
// Send a write request to the sftp server
|
||||||
|
rc = sftp_aio_begin_write(file, buf, to_write, &aio);
|
||||||
|
if (rc == SSH_ERROR) {
|
||||||
|
// handle error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the response of the write request corresponding to
|
||||||
|
// the sftp aio handle stored in the aio variable.
|
||||||
|
bytes_written = sftp_aio_wait_write(&aio);
|
||||||
|
if (bytes_written == SSH_ERROR) {
|
||||||
|
// handle error
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes_written;
|
||||||
|
}
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
@subsection sftp_aio_actual_use Using the sftp aio API to speed up a transfer
|
||||||
|
|
||||||
|
The above examples were provided to introduce the sftp aio API.
|
||||||
|
This is not how the sftp aio API is intended to be used, because the
|
||||||
|
above usage offers no advantage over the synchronous sftp read/write API
|
||||||
|
which does the same thing i.e issue a request and then immediately wait for
|
||||||
|
its response.
|
||||||
|
|
||||||
|
The facility that the sftp aio API provides is that the user can do
|
||||||
|
anything between issuing a request and getting the corresponding response.
|
||||||
|
Any number of operations can be performed after calling sftp_aio_begin_*()
|
||||||
|
[which issues a request] and before calling sftp_aio_wait_*() [which waits
|
||||||
|
for a response]
|
||||||
|
|
||||||
|
The code can leverage this feature by calling sftp_aio_begin_*() multiple times
|
||||||
|
to issue multiple requests before calling sftp_aio_wait_*() to wait for the
|
||||||
|
response of an earlier issued request. This approach will keep a certain number
|
||||||
|
of requests outstanding at the client side.
|
||||||
|
|
||||||
|
After issuing those requests, while the client code does something else (for
|
||||||
|
example waiting for an outstanding request's response, processing an obtained
|
||||||
|
response, issuing another request or any other operation the client wants
|
||||||
|
to perform), at the same time :
|
||||||
|
|
||||||
|
- Some of those outstanding requests may be travelling over the
|
||||||
|
network towards the server.
|
||||||
|
|
||||||
|
- Some of the outstanding requests may have reached the server and may
|
||||||
|
be queued for processing at the server side.
|
||||||
|
|
||||||
|
- Some of the outstanding requests may have been processed and the
|
||||||
|
corresponding responses may be travelling over the network towards the
|
||||||
|
client.
|
||||||
|
|
||||||
|
- Some of the responses corresponding to the outstanding requests may
|
||||||
|
have already reached the client side.
|
||||||
|
|
||||||
|
Clearly in this case, operations that the client performs and operations
|
||||||
|
involved in transfer/processing of a outstanding request can occur in
|
||||||
|
parallel. Also, operations involved in transfer/processing of two or more
|
||||||
|
outstanding requests may also occur in parallel (for example when one request
|
||||||
|
travels to the server, another request's response may be incoming towards the
|
||||||
|
client). Such kind of parallelism makes the overall transfer faster as compared
|
||||||
|
to a transfer performed using the synchronous sftp read/write API.
|
||||||
|
|
||||||
|
When the synchronous sftp read/write API is used to perform a transfer,
|
||||||
|
a strict sequence is followed:
|
||||||
|
|
||||||
|
- The client issues a single read/write request.
|
||||||
|
- Then waits for its response.
|
||||||
|
- On obtaining the response, the client processes it.
|
||||||
|
- After the processing ends, the client issues the next read/write request.
|
||||||
|
|
||||||
|
A file transfer performed in this manner would be slower than the case where
|
||||||
|
multiple read/write requests are kept outstanding at the client side. Because
|
||||||
|
here at any given time, operations related to transfer/processing of only one
|
||||||
|
request/response pair occurs. This is in contrast to the multiple outstanding
|
||||||
|
requests scenario where operations related to transfer/processing of multiple
|
||||||
|
request/response pairs may occur at the same time.
|
||||||
|
|
||||||
|
Although it's true that keeping multiple requests outstanding can speed up a
|
||||||
|
transfer, those outstanding requests come at a cost of increased memory
|
||||||
|
consumption both at the client side and the server side. Hence care must be
|
||||||
|
taken to use a reasonable limit for the number of requests kept outstanding.
|
||||||
|
|
||||||
|
The further sections provide code examples to show how uploads/downloads
|
||||||
|
can be performed using the sftp aio API and the concept of outstanding requests
|
||||||
|
discussed in this section. In those code examples, error handling has been
|
||||||
|
ignored and at some places pseudo code has been used for the sake of brevity.
|
||||||
|
|
||||||
|
The complete code for performing uploads/downloads using the sftp aio API,
|
||||||
|
can be found at https://gitlab.com/libssh/libssh-mirror/-/tree/master.
|
||||||
|
|
||||||
|
- libssh benchmarks for uploads performed using the sftp aio API [See
|
||||||
|
tests/benchmarks/bench_sftp.c]
|
||||||
|
- libssh benchmarks for downloads performed using the sftp aio API. [See
|
||||||
|
tests/benchmarks/bench_sftp.c]
|
||||||
|
- libssh sftp ft API code for performing a local to remote transfer (upload).
|
||||||
|
[See src/sftp_ft.c]
|
||||||
|
- libssh sftp ft API code for performing a remote to local transfer
|
||||||
|
(download). [See src/sftp_ft.c]
|
||||||
|
|
||||||
|
@subsection sftp_aio_download_example Performing a download using the sftp aio API
|
||||||
|
|
||||||
|
Terminologies used in the following code snippets :
|
||||||
|
|
||||||
|
- file : The sftp file handle of the remote file to download data
|
||||||
|
from. (See sftp_open())
|
||||||
|
|
||||||
|
- file_size : the size of the sftp file to download. This size can be obtained
|
||||||
|
by statting the remote file to download (e.g by using sftp_stat())
|
||||||
|
|
||||||
|
- We will need to maintain a queue which will be used to store the sftp aio
|
||||||
|
handles corresponding to the outstanding requests.
|
||||||
|
|
||||||
|
First, we issue the read requests while ensuring that their count
|
||||||
|
doesn't exceed a particular limit decided by us, and the number of bytes
|
||||||
|
requested don't exceed the size of the file to download.
|
||||||
|
|
||||||
|
@code
|
||||||
|
sftp_aio aio = NULL;
|
||||||
|
|
||||||
|
// Using a chunk size of 16 KB
|
||||||
|
size_t chunk_size = 16 * 1024;
|
||||||
|
|
||||||
|
// Max number of requests to keep outstanding at a time
|
||||||
|
size_t in_flight_requests = 5;
|
||||||
|
|
||||||
|
// Number of bytes for which requests have been sent
|
||||||
|
size_t bytes_requested = 0;
|
||||||
|
|
||||||
|
// Number of bytes which have been downloaded
|
||||||
|
size_t bytes_downloaded = 0;
|
||||||
|
|
||||||
|
// Buffer to use for the download
|
||||||
|
char *buffer = NULL;
|
||||||
|
|
||||||
|
buffer = malloc(chunk_size);
|
||||||
|
if (buffer == NULL) {
|
||||||
|
// handle error
|
||||||
|
}
|
||||||
|
|
||||||
|
... // Code to open the remote file (to download) using sftp_open().
|
||||||
|
... // Code to stat the remote file's file size.
|
||||||
|
... // Code to open the local file in which downloaded data is to be stored.
|
||||||
|
... // Code to initialize the queue which will be used to store sftp aio
|
||||||
|
// handles.
|
||||||
|
|
||||||
|
for (i = 0;
|
||||||
|
i < in_flight_requests && bytes_requested < file_size;
|
||||||
|
++i) {
|
||||||
|
to_read = file_size - bytes_requested;
|
||||||
|
if (to_read > chunk_size) {
|
||||||
|
to_read = chunk_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Issue a read request
|
||||||
|
rc = sftp_aio_begin_read(file, to_read, &aio);
|
||||||
|
if (rc == SSH_ERROR) {
|
||||||
|
// handle error
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes_requested += to_read;
|
||||||
|
|
||||||
|
// Pseudo code
|
||||||
|
ENQUEUE aio in the queue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
At this point, at max in_flight_requests number of requests may be
|
||||||
|
outstanding. Now we wait for the response corresponding to the earliest
|
||||||
|
issued outstanding request.
|
||||||
|
|
||||||
|
On getting that response, we issue another read request if there are
|
||||||
|
still some bytes in the sftp file (to download) for which we haven't sent the
|
||||||
|
read request. (This happens when bytes_requested < file_size)
|
||||||
|
|
||||||
|
This issuing of another read request (under a condition) is done to
|
||||||
|
keep the number of outstanding requests equal to the value of the
|
||||||
|
in_flight_requests variable.
|
||||||
|
|
||||||
|
This process has to be repeated for every remaining outstanding request.
|
||||||
|
|
||||||
|
@code
|
||||||
|
while (the queue is not empty) {
|
||||||
|
// Pseudo code
|
||||||
|
aio = DEQUEUE an sftp aio handle from the queue of sftp aio handles;
|
||||||
|
|
||||||
|
// Wait for the response of the request corresponding to the aio
|
||||||
|
bytes_read = sftp_aio_wait_read(&aio, buffer, chunk_size);
|
||||||
|
if (bytes_read == SSH_ERROR) {
|
||||||
|
//handle error
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes_downloaded += bytes_read;
|
||||||
|
if (bytes_read != chunk_size && bytes_downloaded != file_size) {
|
||||||
|
// A short read encountered on the remote file before reaching EOF,
|
||||||
|
// handle it.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pseudo code
|
||||||
|
WRITE bytes_read bytes from the buffer into the local file
|
||||||
|
in which downloaded data is to be stored ;
|
||||||
|
|
||||||
|
if (bytes_requested == file_size) {
|
||||||
|
// no need to issue more read requests
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// else issue a read request
|
||||||
|
to_read = file_size - bytes_requested;
|
||||||
|
if (to_read > chunk_size) {
|
||||||
|
to_read = chunk_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = sftp_aio_begin_read(file, to_read, &aio);
|
||||||
|
if (rc == SSH_ERROR) {
|
||||||
|
// handle error
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes_requested += to_read;
|
||||||
|
|
||||||
|
// Pseudo code
|
||||||
|
ENQUEUE aio in the queue;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(buffer);
|
||||||
|
|
||||||
|
... // Code to destroy the queue which was used to store the sftp aio
|
||||||
|
// handles.
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
After exiting the while (the queue is not empty) loop, the download
|
||||||
|
would've been complete (assuming no error occurs).
|
||||||
|
|
||||||
|
@subsection sftp_aio_upload_example Performing an upload using the sftp aio API
|
||||||
|
|
||||||
|
Terminologies used in the following code snippets :
|
||||||
|
|
||||||
|
- file : The sftp file handle of the remote file in which uploaded data
|
||||||
|
is to be stored. (See sftp_open())
|
||||||
|
|
||||||
|
- file_size : The size of the local file to upload. This size can be
|
||||||
|
obtained by statting the local file to upload (e.g by using stat())
|
||||||
|
|
||||||
|
- We will need maintain a queue which will be used to store the sftp aio
|
||||||
|
handles corresponding to the outstanding requests.
|
||||||
|
|
||||||
|
First, we issue the write requests while ensuring that their count
|
||||||
|
doesn't exceed a particular limit decided by us, and the number of bytes
|
||||||
|
requested to write don't exceed the size of the file to upload.
|
||||||
|
|
||||||
|
@code
|
||||||
|
sftp_aio aio = NULL;
|
||||||
|
|
||||||
|
// Using a chunk size of 16 KB
|
||||||
|
size_t chunk_size = 16 * 1024;
|
||||||
|
|
||||||
|
// Max number of requests to keep outstanding at a time
|
||||||
|
size_t in_flight_requests = 5;
|
||||||
|
|
||||||
|
// Number of bytes for which write requests have been sent
|
||||||
|
size_t bytes_requested = 0;
|
||||||
|
|
||||||
|
// Buffer to use for the upload
|
||||||
|
char *buffer = NULL;
|
||||||
|
|
||||||
|
buffer = malloc(chunk_size);
|
||||||
|
if (buffer == NULL) {
|
||||||
|
// handle error
|
||||||
|
}
|
||||||
|
|
||||||
|
... // Code to open the local file (to upload) [e.g using open(), fopen()].
|
||||||
|
... // Code to stat the local file's file size [e.g using stat()].
|
||||||
|
... // Code to open the remote file in which uploaded data will be stored [see
|
||||||
|
// sftp_open()].
|
||||||
|
... // Code to initialize the queue which will be used to store sftp aio
|
||||||
|
// handles.
|
||||||
|
|
||||||
|
for (i = 0;
|
||||||
|
i < in_flight_requests && bytes_requested < file_size;
|
||||||
|
++i) {
|
||||||
|
to_write = file_size - bytes_requested;
|
||||||
|
if (to_write > chunk_size) {
|
||||||
|
to_write = chunk_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pseudo code
|
||||||
|
READ to_write bytes from the local file (to upload) into the buffer;
|
||||||
|
|
||||||
|
rc = sftp_aio_begin_write(file, buffer, to_write, &aio);
|
||||||
|
if (rc == SSH_ERROR) {
|
||||||
|
// handle error
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes_requested += to_write;
|
||||||
|
|
||||||
|
// Pseudo code
|
||||||
|
ENQUEUE aio in the queue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
At this point, at max in_flight_requests number of requests may be
|
||||||
|
outstanding. Now we wait for the response corresponding to the earliest
|
||||||
|
issued outstanding request.
|
||||||
|
|
||||||
|
On getting that response, we issue another write request if there are
|
||||||
|
still some bytes in the local file (to upload) for which we haven't sent
|
||||||
|
the write request. (This happens when bytes_requested < file_size)
|
||||||
|
|
||||||
|
This issuing of another write request (under a condition) is done to
|
||||||
|
keep the number of outstanding requests equal to the value of the
|
||||||
|
in_flight_requests variable.
|
||||||
|
|
||||||
|
This process has to be repeated for every remaining outstanding request.
|
||||||
|
|
||||||
|
@code
|
||||||
|
while (the queue is not empty) {
|
||||||
|
// Pseudo code
|
||||||
|
aio = DEQUEUE an sftp aio handle from the queue of sftp aio handles;
|
||||||
|
|
||||||
|
// Wait for the response of the request corresponding to the aio
|
||||||
|
bytes_written = sftp_aio_wait_write(&aio);
|
||||||
|
if (bytes_written == SSH_ERROR) {
|
||||||
|
// handle error
|
||||||
|
}
|
||||||
|
|
||||||
|
// sftp_aio_wait_write() won't report a short write, so no need
|
||||||
|
// to check for a short write here.
|
||||||
|
|
||||||
|
if (bytes_requested == file_size) {
|
||||||
|
// no need to issue more write requests
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// else issue a write request
|
||||||
|
to_write = file_size - bytes_requested;
|
||||||
|
if (to_write > chunk_size) {
|
||||||
|
to_write = chunk_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pseudo code
|
||||||
|
READ to_write bytes from the local file (to upload) into a buffer;
|
||||||
|
|
||||||
|
rc = sftp_aio_begin_write(file, buffer, to_write, &aio);
|
||||||
|
if (rc == SSH_ERROR) {
|
||||||
|
// handle error
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes_requested += to_write;
|
||||||
|
|
||||||
|
// Pseudo code
|
||||||
|
ENQUEUE aio in the queue;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(buffer);
|
||||||
|
|
||||||
|
... // Code to destroy the queue which was used to store the sftp aio
|
||||||
|
// handles.
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
After exiting the while (the queue is not empty) loop, the upload
|
||||||
|
would've been complete (assuming no error occurs).
|
||||||
|
|
||||||
|
@subsection sftp_aio_free Example showing the usage of sftp_aio_free()
|
||||||
|
|
||||||
|
The purpose of sftp_aio_free() was discussed at the beginning of this page,
|
||||||
|
the following code example shows how it can be used during cleanup.
|
||||||
|
|
||||||
|
@code
|
||||||
|
void print_sftp_error(sftp_session sftp)
|
||||||
|
{
|
||||||
|
if (sftp == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "sftp error : %d\n", sftp_get_error(sftp));
|
||||||
|
fprintf(stderr, "ssh error : %s\n", ssh_get_error(sftp->session));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns 0 on success, -1 on error
|
||||||
|
int write_strings(sftp_file file)
|
||||||
|
{
|
||||||
|
const char * strings[] = {
|
||||||
|
"This is the first string",
|
||||||
|
"This is the second string",
|
||||||
|
"This is the third string",
|
||||||
|
"This is the fourth string"
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t string_count = sizeof(strings) / sizeof(strings[0]);
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
sftp_session sftp = NULL;
|
||||||
|
sftp_aio aio = NULL;
|
||||||
|
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
if (file == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
... // Code to initialize the queue which will be used to store sftp aio
|
||||||
|
// handles
|
||||||
|
|
||||||
|
sftp = file->sftp;
|
||||||
|
for (i = 0; i < string_count; ++i) {
|
||||||
|
rc = sftp_aio_begin_write(file,
|
||||||
|
strings[i],
|
||||||
|
strlen(strings[i]),
|
||||||
|
&aio);
|
||||||
|
if (rc == SSH_ERROR) {
|
||||||
|
print_sftp_error(sftp);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pseudo code
|
||||||
|
ENQUEUE aio in the queue of sftp aio handles
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < string_count; ++i) {
|
||||||
|
// Pseudo code
|
||||||
|
aio = DEQUEUE an sftp aio handle from the queue of sftp aio handles;
|
||||||
|
|
||||||
|
rc = sftp_aio_wait_write(&aio);
|
||||||
|
if (rc == SSH_ERROR) {
|
||||||
|
print_sftp_error(sftp);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
... // Code to destroy the queue in which sftp aio handles were
|
||||||
|
// stored
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err:
|
||||||
|
|
||||||
|
while (queue is not empty) {
|
||||||
|
// Pseudo code
|
||||||
|
aio = DEQUEUE an sftp aio handle from the queue of sftp aio handles;
|
||||||
|
|
||||||
|
sftp_aio_free(aio);
|
||||||
|
}
|
||||||
|
|
||||||
|
... // Code to destroy the queue in which sftp aio handles were
|
||||||
|
// stored.
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
*/
|
Reference in New Issue
Block a user