diff --git a/doc/introduction.dox b/doc/introduction.dox index 53878248..9026a9d1 100644 --- a/doc/introduction.dox +++ b/doc/introduction.dox @@ -14,6 +14,10 @@ libssh is a Free Software / Open Source project. The libssh library is distributed under LGPL license. The libssh project has nothing to do with "libssh2", which is a completly different and independant project. +libssh can run on top of either libgcrypt (http://directory.fsf.org/project/libgcrypt/) +or libcrypto (http://www.openssl.org/docs/crypto/crypto.html), two general-purpose +cryptographic libraries. + This tutorial concentrates for its main part on the "client" side of libssh. To learn how to accept incoming SSH connexions (how to write a SSH server), you'll have to jump to the end of this document. @@ -35,6 +39,8 @@ Table of contents: @subpage sftp +@subpage scp + @subpage tbd */ diff --git a/doc/scp.dox b/doc/scp.dox new file mode 100644 index 00000000..811b51fd --- /dev/null +++ b/doc/scp.dox @@ -0,0 +1,7 @@ +/** +@page scp Chapter 6: The SCP subsystem +@section scp_subsystem The SCP subsystem + +*** To be written *** + +*/ diff --git a/doc/sftp.dox b/doc/sftp.dox index 1aca34de..6f9bcde7 100644 --- a/doc/sftp.dox +++ b/doc/sftp.dox @@ -25,6 +25,9 @@ that you can do with local files: - create symbolic links - get information about mounted filesystems. +The current implemented version of the SFTP protocol is version 3. All functions +aren't implemented yet, but the most important are. + @subsection sftp_section Opening and closing a SFTP session @@ -35,8 +38,7 @@ The function sftp_new() creates a new SFTP session. The function sftp_init() initializes it. The function sftp_free() deletes it. As you see, all the SFTP-related functions start with the "sftp_" prefix -instead of the usual "ssh_" prefix. In case of a problem, you use -sftp_get_error() instead of ssh_get_error() to get the English error message. +instead of the usual "ssh_" prefix. The example below shows how to use these functions: @@ -71,18 +73,323 @@ int sftp_helloworld(ssh_session session) @endcode +@subsection sftp_errors Analyzing SFTP errors + +In case of a problem, the function sftp_get_error() returns a SFTP-specific +error number, in addition to the regular SSH error number returned by +ssh_get_error_number(). + +Possible errors are: + - SSH_FX_OK: no error + - SSH_FX_EOF: end-of-file encountered + - SSH_FX_NO_SUCH_FILE: file does not exist + - SSH_FX_PERMISSION_DENIED: permission denied + - SSH_FX_FAILURE: generic failure + - SSH_FX_BAD_MESSAGE: garbage received from server + - SSH_FX_NO_CONNECTION: no connection has been set up + - SSH_FX_CONNECTION_LOST: there was a connection, but we lost it + - SSH_FX_OP_UNSUPPORTED: operation not supported by libssh yet + - SSH_FX_INVALID_HANDLE: invalid file handle + - SSH_FX_NO_SUCH_PATH: no such file or directory path exists + - SSH_FX_FILE_ALREADY_EXISTS: an attempt to create an already existing file or directory has been made + - SSH_FX_WRITE_PROTECT: write-protected filesystem + - SSH_FX_NO_MEDIA: no media was in remote drive + + @subsection sftp_mkdir Creating a directory -*** To be written *** +The function sftp_mkdir() tahes the "SFTP session" we juste created as +its first argument. It also needs the name of the file to create, and the +desired permissions. The permissions are the same as for the usual mkdir() +function. To get a comprehensive list of the available permissions, use the +"man 2 stat" command. The desired permissions are combined with the remote +user's mask to determine the effective permissions. + +The code below creates a directory named "helloworld" in the current directory that +can be read and written only by its owner: + +@code +#include +#include + +int sftp_helloworld(ssh_session session, sftp_session sftp) +{ + int rc; + + rc = sftp_mkdir(sftp, "helloworld", S_IRWXU); + if (rc != SSH_OK) + { + if (sftp_get_error(sftp) != SSH_FX_FILE_ALREADY_EXISTS) + { + fprintf(stderr, "Can't create directory: %s\n", ssh_get_error(session)); + return rc; + } + } + + ... + + return SSH_OK; +} +@endcode + +Unlike its equivalent in the SCP subsystem, this function does NOT change the +current directory to the newly created subdirectory. @subsection sftp_write Copying a file to the remote computer -*** To be written *** +You handle the contents of a remote file just like you would do with a +local file: you open the file in a given mode, move the file pointer in it, +read or write data, and close the file. + +The sftp_open() function is very similar to the regular open() function, +excepted that it returns a file handle of type sftp_file. This file handle +is then used by the other file manipulation functions and remains valid +until you close the remote file with sftp_close(). + +The example below creates a new file named "helloworld.txt" in the +newly created "helloworld" directory. If the file already exists, it will +be truncated. It then writes the famous "Hello, World!" sentence to the +file, followed by a new line character. Finally, the file is closed: + +@code +#include +#include +#include + +int sftp_helloworld(ssh_session session, sftp_session sftp) +{ + int access_type = O_WRONLY | O_CREAT | O_TRUNC; + sftp_file file; + const char *helloworld = "Hello, World!\n"; + int length = strlen(helloworld); + int rc, nwritten; + + ... + + file = sftp_open(sftp, "helloworld/helloworld.txt", access_type, S_IRWXU); + if (file == NULL) + { + fprintf(stderr, "Can't open file for writing: %s\n", ssh_get_error(session)); + return SSH_ERROR; + } + + nwritten = sftp_write(file, helloworld, length); + if (nwritten != length) + { + fprintf(stderr, "Can't write data to file: %s\n", ssh_get_error(session)); + sftp_close(file); + return SSH_ERROR; + } + + rc = sftp_close(file); + if (rc != SSH_OK) + { + fprintf(stderr, "Can't close the written file: %s\n", ssh_get_error(session)); + return rc; + } + + return SSH_OK; +} +@endcode @subsection sftp_read Reading a file from the remote computer -*** To be written *** +The nice thing with reading a file over the network through SFTP is that it +can be done both in a synchronous way or an asynchronous way. If you read the file +asynchronously, your program can do something else while it waits for the +results to come. + +Synchronous read is done with sftp_read(). + +The following example prints the contents of remote file "/etc/profile". For +each 1024 bytes of information read, it waits until the end of the read operation: + +@code +int sftp_read_sync(ssh_session session, sftp_session sftp) +{ + int access_type; + sftp_file file; + char buffer[1024]; + int nbytes, rc; + + access_type = O_RDONLY; + file = sftp_open(sftp, "/etc/profile", access_type, 0); + if (file == NULL) + { + fprintf(stderr, "Can't open file for reading: %s\n", ssh_get_error(session)); + return SSH_ERROR; + } + + nbytes = sftp_read(file, buffer, sizeof(buffer)); + while (nbytes > 0) + { + if (write(1, buffer, nbytes) != nbytes) + { + sftp_close(file); + return SSH_ERROR; + } + nbytes = sftp_read(file, buffer, sizeof(buffer)); + } + + if (nbytes < 0) + { + fprintf(stderr, "Error while reading file: %s\n", ssh_get_error(session)); + sftp_close(file); + return SSH_ERROR; + } + + rc = sftp_close(file); + if (rc != SSH_OK) + { + fprintf(stderr, "Can't close the read file: %s\n", ssh_get_error(session)); + return rc; + } + + return SSH_OK; +} +@endcode + +Asynchronous read is done in two steps, first sftp_async_read_begin(), which +returns a "request handle", and then sftp_async_read(), which uses that request handle. +If the file has been opened in nonblocking mode, then sftp_async_read() +might return SSH_AGAIN, which means that the request hasn't completed yet +and that the function should be called again later on. Otherwise, +sftp_async_read() waits for the data to come. To open a file in nonblocking mode, +call sftp_file_set_nonblocking() right after you opened it. Default is blocking mode. + +The example below reads a very big file in asynchronous, nonblocking, mode. Each +time the data are not ready yet, a counter is incrementer. + +@code +int sftp_read_async(ssh_session session, sftp_session sftp) +{ + int access_type; + sftp_file file; + char buffer[1024]; + int async_request; + int nbytes; + long counter; + int rc; + + access_type = O_RDONLY; + file = sftp_open(sftp, "some_very_big_file", access_type, 0); + if (file == NULL) + { + fprintf(stderr, "Can't open file for reading: %s\n", ssh_get_error(session)); + return SSH_ERROR; + } + sftp_file_set_nonblocking(file); + + async_request = sftp_async_read_begin(file, sizeof(buffer)); + counter = 0L; + usleep(10000); + if (async_request >= 0) + nbytes = sftp_async_read(file, buffer, sizeof(buffer), async_request); + else nbytes = -1; + while (nbytes > 0 || nbytes == SSH_AGAIN) + { + if (nbytes > 0) + { + write(1, buffer, nbytes); + async_request = sftp_async_read_begin(file, sizeof(buffer)); + } + else counter++; + usleep(10000); + if (async_request >= 0) + nbytes = sftp_async_read(file, buffer, sizeof(buffer), async_request); + else nbytes = -1; + } + + if (nbytes < 0) + { + fprintf(stderr, "Error while reading file: %s\n", ssh_get_error(session)); + sftp_close(file); + return SSH_ERROR; + } + + printf("The counter has reached value: %ld\n", counter); + + rc = sftp_close(file); + if (rc != SSH_OK) + { + fprintf(stderr, "Can't close the read file: %s\n", ssh_get_error(session)); + return rc; + } + + return SSH_OK; +} +@endcode + +@subsection sftp_ls Listing the contents of a directory + +The functions sftp_opendir(), sftp_readdir(), sftp_dir_eof(), +and sftp_closedir() enable to list the contents of a directory. +They use a new handle_type, "sftp_dir", which gives access to the +directory being read. + +In addition, sftp_readdir() returns a "sftp_attributes" which is a pointer +to a structure with informations about a directory entry: + - name: the name of the file or directory + - size: its size in bytes + - etc. + +sftp_readdir() might return NULL under two conditions: + - when the end of the directory has been met + - when an error occured + +To tell the difference, call sftp_dir_eof(). + +The attributes must be freed with sftp_attributes_free() when no longer +needed. + +The following example reads the contents of some remote directory: + +@code +int sftp_list_dir(ssh_session session, sftp_session sftp) +{ + sftp_dir dir; + sftp_attributes attributes; + int rc; + + dir = sftp_opendir(sftp, "/var/log"); + if (!dir) + { + fprintf(stderr, "Directory not opened: %s\n", ssh_get_error(session)); + return SSH_ERROR; + } + + printf("Name Size Perms Owner\tGroup\n"); + + while ((attributes = sftp_readdir(sftp, dir)) != NULL) + { + printf("%-22s %10llu %.8o %s(%d)\t%s(%d)\n", + attributes->name, + (long long unsigned int) attributes->size, + attributes->permissions, + attributes->owner, + attributes->uid, + attributes->group, + attributes->gid); + + sftp_attributes_free(attributes); + } + + if (!sftp_dir_eof(dir)) + { + fprintf(stderr, "Can't list directory: %s\n", ssh_get_error(session)); + sftp_closedir(dir); + return SSH_ERROR; + } + + rc = sftp_closedir(dir); + if (rc != SSH_OK) + { + fprintf(stderr, "Can't close directory: %s\n", ssh_get_error(session)); + return rc; + } +} +@endcode */ diff --git a/doc/tbd.dox b/doc/tbd.dox index ed4e8825..4ee5ab84 100644 --- a/doc/tbd.dox +++ b/doc/tbd.dox @@ -1,8 +1,31 @@ /** @page tbd To be done -@section scp_subsystem The SCP subsystem +@section errors Handling the errors + +When some function returns an error code, it's always possible to get an +english message describing the problem. + +The function ssh_get_error() returns a pointer to the static error buffer. + +ssh_error_code() returns the error code number : SSH_NO_ERROR, +SSH_REQUEST_DENIED, SSH_INVALID_REQUEST, SSH_CONNECTION_LOST, SSH_FATAL, +or SSH_INVALID_DATA. SSH_REQUEST_DENIED means the ssh server refused your +request, but the situation is recoverable. The others mean something happened +to the connection (some encryption problems, server problems, ...). +SSH_INVALID_REQUEST means the library got some garbage from server, but +might be recoverable. SSH_FATAL means the connection has an important +problem and isn't probably recoverable. + +Most of time, the error returned are SSH_FATAL, but some functions +(generaly the ssh_request_xxx ones) may fail because of server denying request. +In these cases, SSH_REQUEST_DENIED is returned. + +ssh_get_error() and ssh_get_error_code() take a ssh_session as a parameter. +That's for thread safety, error messages that can be attached to a session +aren't static anymore. Any error that happens during ssh_options_xxx() +or ssh_connect() (i.e., outside of any session) can be retrieved by +giving NULL as argument. -*** To be written *** @section threads Working with threads