MochiuWiki : SUSE, EC, PCB
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
検索
個人用ツール
ログイン
Toggle dark mode
名前空間
ページ
議論
表示
閲覧
ソースを閲覧
履歴を表示
Qtの基礎 - SCPのソースを表示
提供: MochiuWiki : SUSE, EC, PCB
←
Qtの基礎 - SCP
あなたには「このページの編集」を行う権限がありません。理由は以下の通りです:
この操作は、次のグループのいずれかに属する利用者のみが実行できます:
管理者
、new-group。
このページのソースの閲覧やコピーができます。
== 概要 == Qtにおいて、簡潔にSSH接続を確立する手順を記載する。<br> <br> * libSSHライブラリ (LGPLライセンス) を使用する。 * libSSH2ライブラリ (3条項BSDライセンス) を使用する。 * <code>QProcess</code>クラスを使用して、外部のSSHプロセスをフォークして接続を確立する。 * wolfSSH *: wolfSSHライブラリは、wolfSSLと連携して使用する。 *: 無償版は、<u>GPL v2ライセンス</u>である。 *: 有償版は、独自ライセンスである。 *: https://wolfssl.jp/products/wolfssh/ *: Github : https://github.com/wolfSSL/wolfssh *: wolfSSLのGithub : https://github.com/wolfSSL/wolfssl *: <br> *: また、wolfSSLは、IoTデバイス、組み込み向け小型デバイスにも対応した軽量のSSL/TLSライブラリである。 * QSshライブラリを使用する。 *: QSshは、QtアプリケーションにSSHとSFTPのサポートを提供する。 *: QSshプロジェクトは、過去のQt CreatorのSSHプラグインに基づいており、全てのクレジットはQt Creatorチームに帰属する。 *: ただし、現在では、Qt CreatorはOpenSSHを使用している。 *: <br> *: https://github.com/sandsmark/QSsh <br><br> == libSSHとlibSSH2の比較 == libSSHとlibSSH2は、どちらもSSHプロトコルを実装したライブラリである。<br> libSSHはC言語、libSSH2はC++言語で記述されている。<br> <br> また、libSSHは単独のライブラリとして提供されているのに対して、libSSH2はlibSSL等の他のライブラリと組み合わせて使用することが多い。<br> <br> プロジェクトの起源と開発者<br> * libSSH *: libSSHは、フランスのBenjamin GilbertとArel Corderoによって開発された。 *: libSSHは、SSHプロトコルの実装を提供しており、クライアントおよびサーバの機能をサポートしている。 *: <br> * libSSH2 *: libSSH2は、curlライブラリの主要な開発者でもあるスウェーデンのDaniel Stenbergによって開発された。 *: libSSH2は、SSHのクライアントおよびサーバ機能を提供することを目的としている。 *: ただし、libSSH2はサーバサイドのサポートは行っていない。 <br> 使用されるコードベース<br> * libSSH *: libSSHは、元々PuTTYの一部として開発され、その後独立したプロジェクトになった。 *: PuTTYは、Windows環境でのSSHクライアントとして広く知られている。 *: <br> * libSSH2 *: libSSH2は、curlライブラリとは独立したプロジェクトとして開発されている。 <br> <center> {| class="wikitable" | style="background-color:#fefefe;" |- ! style="background-color:#66CCFF; width: 50%;" | 項目 ! style="background-color:#66CCFF; width: 25%;" | libSSH2 ! style="background-color:#66CCFF; width: 25%;" | libSSH |- | ライブラリ名 || libssh2.so || libssh.so |- | ライセンス || 3条項BSD || LGPL 2.1 |- | サーバサイドのサポート || No || Yes |- | GSSAPI認証 || No || Yes |- | 楕円曲線鍵の交換 || No || Yes |- | 楕円曲線鍵のホスト鍵 || No || Yes |- | ナイトリーテストによるテストケースの自動化 || No || Yes |- | Stable API || Yes || ほとんどの部分 |- | C言語との互換 || C89 || C99 |- | 厳格な名前空間 || Yes || Yes |- | 全ての関数のマニュアル || Yes || No |- | 全ての関数のDoxygenドキュメント || No || Yes |- | Tutorial || Yes || Yes |- | SSH v1のサポート || No || Yes |} </center> <br><br> == libSSHライブラリのインストール == ==== libSSHとは ==== libSSHの多くは、LGPL 2.1ライセンスである。<br> 一部の機能には、2条項BSDライセンスのものがある。<br> <br> ==== パッケージ管理システムからインストール ==== sudo zypper install libssh-devel <br> ==== ソースコードからインストール ==== libSSHのビルドに必要なライブラリをインストールする。<br> sudo zypper install zlib-devel readline-devel libpcap-devel libopenssl-devel libopenssl-1_1-devel libgcrypt-devel p11-kit-devel libsodium-devel libcmocka-devel doxygen \ # 以下に示すライブラリは不要の可能性あり openpgm-devel ldns-devel zeromq-devel unbound-devel libunwind-devel \ libheimdal-devel libgssglue-devel gssntlmssp-devel <br> [https://www.libssh.org/ libSSHの公式Webサイト]または[https://git.libssh.org/projects/libssh.git libSSH向けのGit]にアクセスして、libSSHのソースコードをダウンロードする。<br> ダウンロードしたファイルを解凍する。<br> tar xf libssh-<バージョン>.tar.xz cd libssh-<バージョン> <br> libSSHをビルドおよびインストールする。<br> mkdir build && cd build cmake .. \ -DCMAKE_INSTALL_PREFIX=/<libSSHのインストールディレクトリ> -DCMAKE_BUILD_TYPE=Release \ -DWITH_GSSAPI=ON -DWITH_DSA=ON -DWITH_GCRYPT=ON \ # オプション -DWITH_PKCS11_URI=ON -DWITH_BLOWFISH_CIPHER=ON \ # オプション -DCMAKE_C_COMPILER=/<GCCのインストールディレクトリ>/bin/gcc \ # オプション -DCMAKE_CXX_COMPILER=/<GCCのインストールディレクトリ>/bin/g++ # オプション make -j $(nproc) make install <br> libSSHの使用例は、以下に示すURLを参考にすること。<br> https://api.libssh.org/stable/libssh_tutorial.html <br><br> == libSSH2ライブラリのインストール == ==== libSSH2とは ==== libSSH2は、3条項BSDライセンスである。(4条項BSDライセンスから、3番目にあった「宣伝条項」を削除したもの)<br> これにより、GPLと互換性が生まれたため、BSDライセンスのソフトウェアをGPLで配布することができる。(その逆は不可)<br> <br> ==== パッケージ管理システムからインストール ==== sudo zypper install libssh2-devel <br> ==== ソースコードからインストール ==== libSSH2のビルドに必要なライブラリをインストールする。<br> sudo zypper install zlib-devel libopenssl-devel libopenssl-1_1-devel <br> [https://www.libssh2.org/ libSSH2の公式Webサイト]または[https://github.com/libssh2/libssh2 Github]にアクセスして、libSSH2のソースコードをダウンロードする。<br> ダウンロードしたソースコードを解凍する。<br> tar xf libssh2.tar.xz cd libssh2 <br> libSSH2をビルドおよびインストールする。<br> mkdir build && cd build # configureスクリプトを使用する場合 ../cofigur --prefix=<libSSH2のインストールディレクトリ> \ --enable-examples-build --disable-debug --with-libz make -j $(nproc) make install # CMakeを使用する場合 cmake .. \ -DCMAKE_INSTALL_PREFIX=/<libssh2のインストールディレクトリ> \ -DLINT=ON -DBUILD_SHARED_LIBS=ON -DCLEAR_MEMORY=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_DEBUG_LOGGING=OFF \ -DCMAKE_C_COMPILER=/<GCCのインストールディレクトリ>/bin/gcc \ # オプション -DCMAKE_CXX_COMPILER=/<GCCのインストールディレクトリ>/bin/g++ # オプション make -j $(nproc) make install <br> libSSH2の使用例は、以下に示すURLを参考にすること。<br> http://www.chaosstuff.com/2013/09/gnome-mplayer-remote-with-qt-and-libssh2.html<br> https://bitbucket.org/nchokoev/qtsshremote<br> <br> Windowsの場合、CMakeおよびVisual Studioを使用してlibSSH2をビルドおよびインストールすることができる。<br> * CMakeを使用する場合 ** 32ビット Windows **: <code>cmake -DBUILD_SHARED_LIBS=ON -DBUILD_EXAMPLES=OFF -DBUILD_TESTING=OFF -A Win32 .. -B "x86"</code> **: <code>cmake --build x86 --config Release</code> **: <br> ** 64ビット Windows **: <code>cmake -DBUILD_SHARED_LIBS=ON -DBUILD_EXAMPLES=OFF -DBUILD_TESTING=OFF -A x64 .. -B "x64"</code> **: <code>cmake --build x64 --config Release</code> **: <br> *: コンパイルされたDLLファイルとlibファイルは、x86とx64のディレクトリのsrc/Releaseディレクトリに配置される。 *: <br> * Visual Studioを使用する場合 *: libSSH2ディレクトリと同階層にプロジェクトを作成する。 *: プロジェクトのプロパティを選択して、プロパティ画面左ペインにある[General] - プロパティ画面右ペインにある[ターゲット名]を"libSSH2_x64"等と入力する。 *: プロパティ画面左ペインにある[リンカ] - [Advanced] - プロパティ画面右ペインにある[インポートライブラリ]プルダウンから[<Inherit from parent or project defaults>]を選択する。 <br> また、ビルドされたDLLファイルは、以下に示すWebサイトからダウンロードできる。<br> https://download.csdn.net/download/sdhongjun/15682389<br> <br><br> == libSSHライブラリを使用したSCPコマンドの使用例 == 以下の例では、公開鍵認証を使用してリモートPCへSSH接続している。<br> <br> <syntaxhighlight lang="make"> # .pro プロジェクトファイル # pkg-configを使用する場合 CONFIG += link_pkgconfig LIBS += \ -L/<libSSHのインストールディレクトリ>/lib64 -lssh # pkg-configを使用しない場合 LIBS += \ -L/<libSSHのインストールディレクトリ>/lib64 -lssh INCLUDEPATH += \ /<libSSHのインストールディレクトリ>/include </syntaxhighlight> <br> <syntaxhighlight lang="c++"> #include <QCoreApplication> #include <QMessageBox> #include <libssh/libssh.h> int VerifyKnownsHost(ssh_session my_ssh_session, QString &strErrMsg); int SCP(ssh_session my_ssh_session); int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); // SSHセッションの作成 ssh_session my_ssh_session = ssh_new(); if (my_ssh_session == NULL) { // SSHセッションの作成に失敗した場合 return -1; } // SSHセッションの設定 QString host = "<リモートPCのIPアドレス または ホスト名>"; QString user = "<リモートPCのユーザ名>"; QString port = "<SSHポート番号 例: 22>"; ssh_options_set(my_ssh_session, SSH_OPTIONS_HOST, host.toUtf8().data()); ssh_options_set(my_ssh_session, SSH_OPTIONS_USER, user.toUtf8().data()); ssh_options_set(my_ssh_session, SSH_OPTIONS_PORT_STR, port.toUtf8().data()); // SSH接続 int rc = ssh_connect(my_ssh_session); if (rc != SSH_OK) { // 接続に失敗した場合 fprintf(stderr, "Error connecting to host: %s\n", ssh_get_error(my_ssh_session)); ssh_free(my_ssh_session); return -1; } // ~/.sshディレクトリ等にあるファイルに記述されているサーバのIDを検証 QString strErrMsg = ""; if (VerifyKnownsHost(my_ssh_session, strErrMsg) < 0) { fprintf(stderr, "%s\n", strErrMsg.toUtf8().constData(); if (my_ssh_session != nullptr) { ssh_disconnect(my_ssh_session); ssh_free(my_ssh_session); } return -1; } // 公開鍵認証 // 秘密鍵の設定 const char *private_key_path = "<秘密鍵のパス 例: /home/user/sshkey/id_rsa"; // 秘密鍵のパスフレーズを設定していない場合 rc = ssh_userauth_privatekey_file(my_ssh_session, nullptr, private_key_path, nullptr); // 秘密鍵のパスフレーズを設定している場合 rc = ssh_userauth_privatekey_file(my_ssh_session, nullptr, private_key_path, "<秘密鍵のパスフレーズ>"); if (rc != SSH_AUTH_SUCCESS) { // 認証に失敗した場合 fprintf(stderr, "Error authenticating with private key: %s\n", ssh_get_error(my_ssh_session)); ssh_disconnect(my_ssh_session); ssh_free(my_ssh_session); return -1; } // SSHセッションを使用して、SCPコマンドを実行 if (SCP(my_ssh_session)) { // SSH接続の切断 ssh_disconnect(my_ssh_session); // SSHセッションの解放 ssh_free(my_ssh_session); return -1; } // SSH接続の切断 ssh_disconnect(my_ssh_session); // SSHセッションの解放 ssh_free(my_ssh_session); return 0; } int VerifyKnownsHost(ssh_session my_ssh_session, QString &strErrMsg) { // Authenticating the server. ssh_key srv_pubkey = {}; if (ssh_get_server_publickey(my_ssh_session, &srv_pubkey) < 0) { strErrMsg = tr("Failed to get public key."); return -1; } unsigned char *hash = nullptr; size_t hlen = 0L; auto iRet = ssh_get_publickey_hash(srv_pubkey, SSH_PUBLICKEY_HASH_SHA256, &hash, &hlen); ssh_key_free(srv_pubkey); if (iRet < 0) { strErrMsg = tr("Failed to get public key hash."); return -1; } auto state = ssh_session_is_known_server(my_ssh_session); if (state == ssh_known_hosts_e::SSH_KNOWN_HOSTS_OK) { // Authentication Successful } else if (state == ssh_known_hosts_e::SSH_KNOWN_HOSTS_CHANGED) { QString strHexa = ssh_get_hexa(hash, hlen); // print string in reverse order strErrMsg = tr("Host key for server changed:") + "<br>" + tr("For security reasons, connection will be stopped.") + "<br><br>" + tr("Public key hash:") + "<br>" + strHexa + "<br>" + hlen; ssh_clean_pubkey_hash(&hash); return -1; } else if (state == ssh_known_hosts_e::SSH_KNOWN_HOSTS_OTHER) { strErrMsg = tr("The host key for this server was not found but an other type of key exists.") + "<br>" + tr("An attacker might change the default server key to confuse your client into") + "<br>" + tr("thinking the key does not exist"); ssh_clean_pubkey_hash(&hash); return -1; } else if (state == ssh_known_hosts_e::SSH_KNOWN_HOSTS_NOT_FOUND) { /* FALL THROUGH to SSH_KNOWN_HOSTS_UNKNOWN behavior */ QString strHexa = ssh_get_hexa(hash, hlen); QString strAddHostMessage = tr("Could not find known host file.") + "\n" + tr("If you accept the host key here, the file will be automatically created.") + "\n\n" + tr("The server is unknown. Do you trust the host key?") + "\n" + tr("Public key hash: ") + "\n" + strHexa; auto ret = QMessageBox(QMessageBox::Warning, QMessageBox::tr("Add Host"), strAddHostMessage, QMessageBox::Yes | QMessageBox::No, nullptr).exec(); ssh_clean_pubkey_hash(&hash); if(ret == QMessageBox::No) { strErrMsg = tr("To connect, please add host key."); return -1; } else { iRet = ssh_session_update_known_hosts(my_ssh_session); if (iRet < 0) { strErrMsg = tr("Failed to update host key."); return -1; } } } else if (state == ssh_known_hosts_e::SSH_KNOWN_HOSTS_UNKNOWN) { QString strHexa = ssh_get_hexa(hash, hlen); QString strAddHostMessage = tr("The server is unknown. Do you trust the host key?") + "\n" + tr("Public key hash: ") + "\n" + strHexa; auto msgRet = QMessageBox(QMessageBox::Warning, QMessageBox::tr("Add Host"), strAddHostMessage, QMessageBox::Yes | QMessageBox::No, nullptr).exec(); ssh_clean_pubkey_hash(&hash); if (msgRet == QMessageBox::No) { strErrMsg = tr("To connect, please add host key."); return -1; } else { iRet = ssh_session_update_known_hosts(my_ssh_session); if (iRet < 0) { strErrMsg = tr("Failed to update host key."); return -1; } } } else if (state == ssh_known_hosts_e::SSH_KNOWN_HOSTS_ERROR) { strErrMsg = tr("There was an error in checking the host."); ssh_clean_pubkey_hash(&hash); return -1; } ssh_clean_pubkey_hash(&hash); return 0; } int SCP(ssh_session my_ssh_session) { // SCPセッションの作成 ssh_scp scp = ssh_scp_new(my_ssh_session, SSH_SCP_WRITE, "<送信先のリモートPCのパス>"); if (scp == NULL) { fprintf(stderr, "SCPセッションの作成に失敗: %s\n", ssh_get_error(my_ssh_session)); return -1; } // SCPセッションの初期化 rc = ssh_scp_init(scp); if (rc != SSH_OK) { fprintf(stderr, "SCPセッションの作成の初期化に失敗: %s\n", ssh_get_error(my_ssh_session)); ssh_scp_free(scp); return -1; } // ローカルPC上のファイルをオープン QFile file("<送信元のファイルパス>"); if (!file.open(QIODevice::ReadOnly)) { fprintf(stderr, "ファイルのオープンに失敗\n"); ssh_scp_free(scp); return -1; } // ファイル内容を読み込む QByteArray fileContent = file.readAll(); // ファイルの転送 rc = ssh_scp_push_file(scp, filename, fileContent.size(), S_IRUSR | S_IWUSR); if (rc != SSH_OK) { fprintf(stderr, "送信先のファイルのオープンに失敗: %s\n", ssh_get_error(my_ssh_session)); ssh_scp_free(scp); return -1; } rc = ssh_scp_write(scp, fileContent.constData(), fileContent.size()); if (rc != SSH_OK) { fprintf(stderr, "送信先のファイルの書き込みに失敗: %s\n", ssh_get_error(my_ssh_session)); ssh_scp_free(scp); return -1; } // SCPセッションの終了 ssh_scp_close(scp); ssh_scp_free(scp); return 0; } </syntaxhighlight> <br><br> == libSSH2ライブラリを使用したSCPコマンドの使用例 == ==== ブロッキングモード ==== 以下の例では、リモート側のPCにSSH接続して、SCPでファイルを送信している。<br> <br> 以下の例は、<u>ブロッキングモード</u>でSCPを実行している。<br> ノンブロッキングモードを有効にする場合、libSSH2ライブラリの関数呼び出しは即座に返されて、処理がバックグラウンドで非同期に進行する。<br> これにより、プログラムは他の処理を続行でき、必要に応じてリモートホストとの通信が完了するのを待つことができる。<br> <br> ノンブロッキングモードを使用する場合、特に入出力操作が発生する待ち時間を最小限に抑え、プログラムがより効率的に動作することが期待される。<br> しかし、ノンブロッキングモードを扱う際には、非同期処理やイベント駆動型のプログラミングに慣れる必要がある。<br> <br> 以下の例では、GNU LIBCを使用している場合およびQTcpSocketクラスを使用している場合の2つを、<code>#define NOQTCPSOCKET 1</code>プリプロセッサを使用して処理を別けている。<br> <br> 有償版Qtライセンスを購入している場合、GNU LIBCライブラリを使用せずにQTcpSocketクラスを使用するならば、ライセンスにおいてリバースエンジニアリングを禁止することができる。<br> <br> GNU LIBCを使用している場合、(<code>#define NOQTCPSOCKET 1</code>プリプロセッサの使用する場合)<br> インクルードしているLinux向けライブラリ群はglibc-devel (libc-dev) パッケージに含まれるため、LGPLライセンスであることに注意する。<br> * sys/socket.h *: ソケットプログラミングに必要なヘッダファイルである。 * arpa/inet.h *: IPv4やIPv6アドレスの変換やネットワークバイトオーダーとホストバイトオーダーの変換等、ネットワークプログラミングで使用される。 * netinet/in.h *: ネットワークプログラミングやソケットプログラミングに必要なヘッダファイルである。 * unistd.h *: UNIXシステムのシステムコールやその他の標準的なシステム関数を宣言するためのヘッダファイルである。 *: ファイル操作、プロセス管理、システム情報の取得、メモリ管理、ファイルシステムの操作等の関数やマクロが含まれている。 * fcntl.h *: ファイル制御やファイルディスクリプタ関連の操作に使用される。 <br> <syntaxhighlight lang="make"> # .pro プロジェクトファイル # QTcpSocketを使用する場合 QT += network # pkg-configを使用する場合 CONFIG += link_pkgconfig LIBS += \ -L/<libSSH2のインストールディレクトリ>/lib64 -lssh2 # pkg-configを使用しない場合 LIBS += \ -L/<libSSH2のインストールディレクトリ>/lib64 -lssh2 INCLUDEPATH += \ /<libSSH2のインストールディレクトリ>/include </syntaxhighlight> <br> <syntaxhighlight lang="c++"> // main.cpp #include <QCoreApplication> #include <QFileInfo> #include <QTimer> #include <QElapsedTimer> #include <QDebug> #include <libssh2.h> #include <libssh2_sftp.h> #include "DivideByZeroException.h" #define NOQTCPSOCKET 1 #if NOQTCPSOCKET // QTcpSocketクラスを使用しない場合 #ifdef Q_OS_LINUX #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <fcntl.h> #endif #else // QTcpSocketクラスを使用する場合 #include <QTcpSocket> #endif #if NOQTCPSOCKET // QTcpSocketクラスを使用しない場合 void DisConnect(int &sock, LIBSSH2_SESSION *session); int Send(int &sock, LIBSSH2_SESSION *session); int waitsocket(libssh2_socket_t socket_fd, LIBSSH2_SESSION *session); #else // QTcpSocketクラスを使用する場合 void DisConnect(QTcpSocket &sock, LIBSSH2_SESSION *session); int Send(QTcpSocket &sock, LIBSSH2_SESSION *session); #endif int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); int rc = 0; #if NOQTCPSOCKET int sock = 0; #if defined(Q_OS_WIN32) || defined(Q_OS_WIN64) WSADATA wsadata; rc = WSAStartup(MAKEWORD(2, 0), &wsadata); if(rc) { std::cerr << "WSAStartup failed with error: " << rc << std::endl; return -1; } #endif #else QTcpSocket sock; #endif LIBSSH2_SESSION *session = nullptr; // 初期化 rc = libssh2_init(0); if (rc != 0) { qDebug() << QString("libssh2 initialization failed %1").arg(rc); DisConnect(sock, session); return -1; } #if NOQTCPSOCKET // QTcpSocketクラスを使用しない場合 // ソケットの作成 sock = socket(AF_INET, SOCK_STREAM, 0); if (sock == -1) { qDebug() << QString("failed to create socket."); DisConnect(sock, session); return -1; } struct sockaddr_in sin = {}; sin.sin_family = AF_INET; sin.sin_port = htons(<SSHのポート番号>); sin.sin_addr.s_addr = inet_addr("<リモート側PCのIPアドレス または ホスト名>"); // SSH接続 if (connect(sock, (struct sockaddr*)(&sin), sizeof(struct sockaddr_in)) == -1) { qDebug() << QString("failed to connect."); DisConnect(sock, session); return -1; } #else // QTcpSocketクラスを使用する場合 QTcpSocket sock; sock.connectToHost(host, port); if (!sock.waitForConnected()) { qDebug() << "Failed to connect"; DisConnect(sock, session); return -1; } #endif // libSSH2のセッションを初期化 session = libssh2_session_init(); if (!session) { qDebug() << "Failed to initialize SSH session"; DisConnect(sock, session); return -1; } #if NOQTCPSOCKET // QTcpSocketクラスを使用しない場合 // リモート側のPCとのハンドシェイク if (libssh2_session_handshake(session, sock)) { qDebug() << "SSH handshake failed"; DisConnect(sock, session); return -1; } #else // QTcpSocketクラスを使用する場合 if (libssh2_session_handshake(session, sock.socketDescriptor())) { qDebug() << "SSH handshake failed"; DisConnect(sock, session); return -1; } #endif // パスワード認証 if (libssh2_userauth_password(session, "<リモート側のユーザ名>", "<ユーザ名のパスワード>") != 0) { qDebug() << QString("Authentication failed."); DisConnect(sock, session); return -1; } qDebug() << QString("Authentication succeeded."); // SSH接続後の処理 // SCPでファイルを送信 rc = Send(sock, session); if (rc != 0) { DisConnect(sock, session); return -1; } // SSH接続の終了 DisConnect(sock, session); return 0; } #if NOQTCPSOCKET // QTcpSocketクラスを使用しない場合 void DisConnect(int &sock, LIBSSH2_SESSION *session) { // セッションの終了 if (session) { libssh2_session_disconnect(session, "Normal Shutdown"); libssh2_session_free(session); session = nullptr; } // ソケットを閉じる if (sock != LIBSSH2_INVALID_SOCKET) { shutdown(sock, 2); #if defined(Q_OS_WIN32) || defined(Q_OS_WIN64) closesocket(sock); #else close(sock); #endif } // libSSH2の終了 libssh2_exit(); return; } #else // QTcpSocketクラスを使用する場合 void DisConnect(QTcpSocket &sock, LIBSSH2_SESSION *session) { // セッションの終了 if (session) { libssh2_session_disconnect(session, "Normal Shutdown"); libssh2_session_free(session); session = nullptr; } // ソケットを閉じる if(sock.isOpen()) { sock.close(); } // libSSH2の終了 libssh2_exit(); return; } #endif #if NOQTCPSOCKET // QTcpSocketクラスを使用しない場合 int Send(int &sock, LIBSSH2_SESSION *session) #else int Send(QTcpSocket &sock, LIBSSH2_SESSION *session) #endif { // Send a file via scp. The mode parameter must only have permissions. LIBSSH2_CHANNEL *channel = nullptr; QString localFilePath = "<ローカルPCのファイルパス 例: /tmp/hoge.png>"; QFile File(localFilePath); if(!File.open(QIODevice::ReadOnly)) { qDebug() << QString("Cannot open local File(%1) Open Error: %2").arg(QFileInfo(File).fileName(), File.errorString()); return -1; } // QFileInfoクラスではパーミッションは16進数のため、libssh2_scp_send64関数で使用するため8進数に変換する // これは、見た目の数値を同じにする必要がある 例: 0x644(16進数) --> 0644(8進数) QFileInfo FileInfo(localFilePath); //// ステッキービットは不要なため削除 auto hexPermisshionString = QString::number(FileInfo.permissions(), 16).mid(1); bool ok; //// 8進数に変換 unsigned int octPermisshionValue = hexPermisshionString.toInt(&ok, 8); if (!ok) { qDebug() << QString("Failed to convert permission value."); return -1; } QString remoteFilePath = "<リモート側PCのファイルパス 例: /home/remote-user/hoge.png>"; do { channel = libssh2_scp_send64(session, remoteFilePath.toUtf8().constData(), octPermisshionValue & 0777, FileInfo.size(), 0, 0); if (!channel && libssh2_session_last_errno(session) != LIBSSH2_ERROR_EAGAIN) { File.close(); // チャンネルの終了 if (channel) { libssh2_channel_free(channel); channel = nullptr; } char *err_msg; libssh2_session_last_error(session, &err_msg, NULL, 0); qDebug() << err_msg; return -1; } } while (!channel); qDebug() << QString("SCP session waiting to send file."); // タイマの開始 QElapsedTimer elapsedTimer; elapsedTimer.start(); int nread = 0, total = 0; char mem[1024 * 100] = {}; do { nread = File.read(mem, sizeof(mem)); if(nread <= 0) { // EOF break; } auto ptr = mem; total += nread; auto prev = 0; do { ssize_t nwritten; #if NOQTCPSOCKET // QTcpSocketクラスを使用しない場合 while ((nwritten = libssh2_channel_write(channel, ptr, nread)) == LIBSSH2_ERROR_EAGAIN) { waitsocket(sock, session); prev = 0; } #else // QTcpSocketクラスを使用する場合 while ((nwritten = libssh2_channel_write(channel, ptr, nread)) == LIBSSH2_ERROR_EAGAIN) { prev = 0; } // 書き込み完了を待機 sock.waitForBytesWritten(); #endif if (nwritten < 0) { qDebug() << QString("ERROR %1 total %2 / %3 prev %4").arg((int)nwritten) .arg((long)total) .arg((int)nread) .arg((int)prev); break; } else { prev = nread; // nwritten indicates how many bytes were written this time. nread -= nwritten; ptr += nwritten; } } while (nread); } while (!nread); // only continue if nread was drained. File.close(); // 経過時間をミリ秒単位で取得 try { qint64 duration = elapsedTimer.elapsed() == 0 ? throw DivideByZeroException() : elapsedTimer.elapsed(); qDebug() << QString("%1 bytes in %2 milli-seconds makes %3 bytes/sec").arg(static_cast<long>(total)) .arg(duration) .arg(QString::number((double)((total * 1000) / duration), 'f', 1)); } catch (const DivideByZeroException &ex) { qDebug() << ex.what(); qDebug() << QString("Transfer rate could not be calculated."); } qDebug() << QString("Sending EOF"); while(libssh2_channel_send_eof(channel) == LIBSSH2_ERROR_EAGAIN); qDebug() << QString("Waiting for EOF"); while(libssh2_channel_wait_eof(channel) == LIBSSH2_ERROR_EAGAIN); qDebug() << QString("Waiting for channel to close"); while(libssh2_channel_wait_closed(channel) == LIBSSH2_ERROR_EAGAIN); return 0; } #if NOQTCPSOCKET // QTcpSocketクラスを使用しない場合 int waitsocket(libssh2_socket_t socket_fd, LIBSSH2_SESSION *session) { struct timeval timeout = {}; timeout.tv_sec = 10; timeout.tv_usec = 0; fd_set fd; FD_ZERO(&fd); FD_SET(socket_fd, &fd); // now make sure we wait in the correct direction. auto dir = libssh2_session_block_directions(session); fd_set *writefd = nullptr; fd_set *readfd = nullptr; if(dir & LIBSSH2_SESSION_BLOCK_INBOUND) readfd = &fd; if(dir & LIBSSH2_SESSION_BLOCK_OUTBOUND) writefd = &fd; auto rc = select((int)(socket_fd + 1), readfd, writefd, nullptr, &timeout); return rc; } #endif </syntaxhighlight> <br> <syntaxhighlight lang="c++"> // DivideByZeroException.h #ifndef DIVIDEBYZEROEXCEPTION_H #define DIVIDEBYZEROEXCEPTION_H #include <QException> class DivideByZeroException : public QException { public: DivideByZeroException() {}; virtual ~DivideByZeroException() {}; const char* what() const noexcept override { return "Divide by zero exception"; } }; #endif // DIVIDEBYZEROEXCEPTION_H </syntaxhighlight> <br><br> __FORCETOC__ [[カテゴリ:Qt]]
Qtの基礎 - SCP
に戻る。
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
Collapse