Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
YottaChain
YTBP
提交
f5b384f5
Y
YTBP
项目概览
YottaChain
/
YTBP
通知
0
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
Y
YTBP
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
f5b384f5
编写于
5月 31, 2017
作者:
D
Daniel Larimer
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
removing ssh from fc
上级
c2f9a372
变更
6
展开全部
隐藏空白更改
内联
并排
Showing
6 changed file
with
0 addition
and
1607 deletion
+0
-1607
libraries/fc/include/fc/ssh/client.hpp
libraries/fc/include/fc/ssh/client.hpp
+0
-193
libraries/fc/include/fc/ssh/error.hpp
libraries/fc/include/fc/ssh/error.hpp
+0
-25
libraries/fc/include/fc/ssh/process.hpp
libraries/fc/include/fc/ssh/process.hpp
+0
-58
libraries/fc/src/ssh/client.cpp
libraries/fc/src/ssh/client.cpp
+0
-717
libraries/fc/src/ssh/client_impl.hpp
libraries/fc/src/ssh/client_impl.hpp
+0
-280
libraries/fc/src/ssh/process.cpp
libraries/fc/src/ssh/process.cpp
+0
-334
未找到文件。
libraries/fc/include/fc/ssh/client.hpp
已删除
100644 → 0
浏览文件 @
c2f9a372
#pragma once
#include <memory>
#include <functional>
#include <fc/filesystem.hpp>
namespace
fc
{
class
path
;
class
logger
;
namespace
ssh
{
namespace
detail
{
class
client_impl
;
class
process_impl
;
};
enum
sftp_file_type
{
named_pipe
=
0010000
,
directory
=
0040000
,
regular
=
0100000
,
symlink
=
0120000
};
enum
sftp_file_mode
{
owner_mask
=
0000700
,
/* RWX mask for owner */
owner_read
=
0000400
,
/* R for owner */
owner_write
=
0000200
,
/* W for owner */
owner_exec
=
0000100
,
/* X for owner */
group_mask
=
0000070
,
/* RWX mask for group */
group_read
=
0000040
,
/* R for group */
group_write
=
0000020
,
/* W for group */
group_exec
=
0000010
,
/* X for group */
other_mask
=
0000007
,
/* RWX mask for other */
other_read
=
0000004
,
/* R for other */
other_write
=
0000002
,
/* W for other */
other_exec
=
0000001
/* X for other */
};
struct
file_attrib
{
file_attrib
();
uint64_t
size
;
uint32_t
uid
;
uint32_t
gid
;
uint32_t
permissions
;
uint32_t
atime
;
uint32_t
mtime
;
bool
exists
();
bool
is_file
();
bool
is_directory
();
};
/**
* @brief Enables communication over ssh using libssh2.
*
* Because the client creates other resources that depend upon
* it, it can only be created as a std::shared_ptr<client> (aka client::ptr)
* via client::create();
*
*/
class
client
{
public:
enum
trace_level
{
TRACE_NONE
=
0
,
TRACE_TRANS
=
(
1
<<
1
),
TRACE_KEX
=
(
1
<<
2
),
TRACE_AUTH
=
(
1
<<
3
),
TRACE_CONN
=
(
1
<<
4
),
TRACE_SCP
=
(
1
<<
5
),
TRACE_SFTP
=
(
1
<<
6
),
TRACE_ERROR
=
(
1
<<
7
),
TRACE_PUBLICKEY
=
(
1
<<
8
),
TRACE_SOCKET
=
(
1
<<
9
)
};
/**
* Everything but TRACE_ERROR will be logged at fc::log_level::debug, while
* TRACE_ERROR will be logged at fc::log_level::error
*
* @param bitmask comprised of values from trace_level
**/
void
set_trace_level
(
int
bitmask
);
int
get_trace_level
()
const
;
/**
* Override the default logger used by fc::ssh::client
*/
void
set_logger
(
const
logger
&
lgr
);
const
logger
&
get_logger
()
const
;
/**
* Connect, with no password specified. Authentication will try public key,
* (via agent or explicitly-set key), empty password, then keyboard-interactive
*/
void
connect
(
const
fc
::
string
&
user
,
const
fc
::
string
&
host
,
uint16_t
port
=
22
);
/**
* Connect, specifying a password to be used for password authentication
*/
void
connect
(
const
fc
::
string
&
user
,
const
fc
::
string
&
pass
,
const
fc
::
string
&
host
,
uint16_t
port
=
22
);
/**
* @note THIS METHOD IS DEPRECATED and should be replace with:
*
* ssh::client_ptr sshc = std::make_shared<ssh::client>();
* sshc->connect( ... )
* ssh::process_ptr proc = std::make_shared<ssh::process>( sshc );
* proc->exec( ... )
*
*
* @brief execute command on remote machine
* @param pty_type - whether or not to request a PTY when executing this process, this is necessary
* for interactive (non-buffered) IO with the remote process, if left empty no pty will be
* requested
*
* @note Processes launched in this manner will fully buffer stdin and stdout regardless of whether
* the process calls flush(). If you need unbuffered (streaming, realtime) access to standard
* out then you must launch the process via a shell.
ssh::process exec( const fc::string& cmd, const fc::string& pty_type = "" );
*/
/**
* @brief upload a file to remote host
* @param progress a callback to report / cancel upload.
* The callback takes two parameters, bytes sent and file size. To continue the
* transfer, the callback should return true. To cancel the callback should return false.
*/
void
scp_send
(
const
fc
::
path
&
local_path
,
const
fc
::
path
&
remote_path
,
std
::
function
<
bool
(
uint64_t
,
uint64_t
)
>
progress
=
[](
uint64_t
,
uint64_t
){
return
true
;}
);
/**
* @brief recursively sends the contents of local_dir to the remote_path
*
* If remote_path ends in '/' then a new directory at <code>remote_path/local_dir.filename()</code> will
* be created, otherwise <code>local_dir / *</code> will be copied to <code>remote_path / *</code>
*
* Progress will be reported as total bytes transferred for all files.
*/
void
scp_send_dir
(
const
fc
::
path
&
local_dir
,
const
fc
::
path
&
remote_path
,
std
::
function
<
bool
(
uint64_t
,
uint64_t
)
>
progress
=
[](
uint64_t
,
uint64_t
){
return
true
;}
);
/**
* @pre remote_path is not a directory
* @post remote file is removed from the remote filesystem
*/
void
rm
(
const
fc
::
path
&
remote_path
);
/**
* @pre remote_path is a directory
* @post remote directory is removed from the remote filesystem
*/
void
rmdir
(
const
fc
::
path
&
remote_path
);
void
rmdir_recursive
(
const
fc
::
path
&
remote_path
);
file_attrib
stat
(
const
fc
::
path
&
remote_path
);
/**
* @pre all parent directories already exist.
* @pre remote_dir is not exist or is already a directory
* @post remote_dir exists.
*/
void
mkdir
(
const
fc
::
path
&
remote_dir
,
int
mode
=
owner_read
|
owner_write
|
owner_exec
);
/**
* Create all parent directories for remote_dir if they do not exist.
*
* @post remote_dir exists.
*/
void
create_directories
(
const
fc
::
path
&
remote_dir
,
int
mode
=
owner_read
|
owner_write
|
owner_exec
);
/**
* Sets whether the remote system is believed to be a Windows box (by default, it's
* assumed to be running UNIX. This alters how command-line arguments are quoted
* and possibly how filenames are altered when copying files
*/
void
set_remote_system_is_windows
(
bool
is_windows
=
true
);
void
close
();
client
();
~
client
();
private:
friend
class
process
;
friend
class
detail
::
process_impl
;
std
::
unique_ptr
<
detail
::
client_impl
>
my
;
};
typedef
std
::
shared_ptr
<
client
>
client_ptr
;
}
}
// namespace fc::ssh
libraries/fc/include/fc/ssh/error.hpp
已删除
100644 → 0
浏览文件 @
c2f9a372
#ifndef MACE_SSH_ERROR_HPP
#define MACE_SSH_ERROR_HPP
#include <boost/exception/all.hpp>
#include <boost/format.hpp>
namespace
mace
{
namespace
ssh
{
typedef
boost
::
error_info
<
struct
err_msg_
,
std
::
string
>
err_msg
;
struct
exception
:
public
virtual
boost
::
exception
,
public
virtual
std
::
exception
{
const
char
*
what
()
const
throw
()
{
return
"exception"
;
}
virtual
void
rethrow
()
const
{
BOOST_THROW_EXCEPTION
(
*
this
);
}
const
std
::
string
&
message
()
const
{
return
*
boost
::
get_error_info
<
mace
::
ssh
::
err_msg
>
(
*
this
);
}
};
}
}
// mace::ssh
/**
* Helper macro for throwing exceptions with a message: THROW( "Hello World %1%, %2%", %"Hello" %"World" )
*/
#define MACE_SSH_THROW( MSG, ... ) \
do { \
BOOST_THROW_EXCEPTION( mace::ssh::exception() << mace::ssh::err_msg( (boost::format( MSG ) __VA_ARGS__ ).str() ) );\
} while(0)
#endif
libraries/fc/include/fc/ssh/process.hpp
已删除
100644 → 0
浏览文件 @
c2f9a372
#pragma once
#include <fc/interprocess/iprocess.hpp>
namespace
fc
{
namespace
ssh
{
class
client
;
namespace
detail
{
class
process_impl
;
};
/**
* Enables communication with a process executed via
* client::exec().
*
* Process can only be created by mace::ssh::client.
*/
class
process
:
public
iprocess
{
public:
virtual
iprocess
&
exec
(
const
fc
::
path
&
exe
,
std
::
vector
<
std
::
string
>
args
,
const
fc
::
path
&
work_dir
=
fc
::
path
(),
exec_opts
opts
=
open_all
);
/**
* Blocks until the result code of the process has been returned.
*/
virtual
int
result
();
/**
* Not supported. libssh2 does not support sending signals to remote processes.
* closing in_stream() is the best you can do
*/
virtual
void
kill
();
/**
* @brief returns a stream that writes to the process' stdin
*/
virtual
fc
::
buffered_ostream_ptr
in_stream
();
/**
* @brief returns a stream that reads from the process' stdout
*/
virtual
fc
::
buffered_istream_ptr
out_stream
();
/**
* @brief returns a stream that reads from the process' stderr
*/
virtual
fc
::
buffered_istream_ptr
err_stream
();
process
(
fc
::
ssh
::
client_ptr
c
);
~
process
();
private:
std
::
unique_ptr
<
detail
::
process_impl
>
my
;
};
}
}
// fc::ssh
libraries/fc/src/ssh/client.cpp
已删除
100644 → 0
浏览文件 @
c2f9a372
此差异已折叠。
点击以展开。
libraries/fc/src/ssh/client_impl.hpp
已删除
100644 → 0
浏览文件 @
c2f9a372
#define NOMINMAX
#include <libssh2.h>
#include <libssh2_sftp.h>
#include <boost/asio.hpp>
#include <fc/ssh/client.hpp>
#include <fc/ssh/process.hpp>
#include <fc/thread/mutex.hpp>
#include <fc/thread/spin_lock.hpp>
#include <fc/thread/scoped_lock.hpp>
#include <fc/log/logger.hpp>
#include <fc/asio.hpp>
// include this to get acess to the details of the LIBSSH2_SESSION structure, so
// we can verify that all data has really been sent when libssh2 says it has.
#include <../src/libssh2_priv.h>
namespace
fc
{
namespace
ssh
{
namespace
detail
{
class
client_impl
{
public:
client_impl
();
~
client_impl
();
LIBSSH2_SESSION
*
session
;
LIBSSH2_KNOWNHOSTS
*
knownhosts
;
LIBSSH2_SFTP
*
sftp
;
LIBSSH2_AGENT
*
agent
;
std
::
unique_ptr
<
boost
::
asio
::
ip
::
tcp
::
socket
>
sock
;
boost
::
asio
::
ip
::
tcp
::
endpoint
endpt
;
fc
::
mutex
ssh_session_mutex
;
fc
::
mutex
channel_open_mutex
;
fc
::
mutex
process_startup_mutex
;
fc
::
mutex
scp_send_mutex
;
fc
::
mutex
scp_stat_mutex
;
fc
::
mutex
scp_mkdir_mutex
;
fc
::
mutex
scp_rmdir_mutex
;
fc
::
mutex
scp_unlink_mutex
;
fc
::
mutex
scp_close_mutex
;
fc
::
mutex
scp_readdir_mutex
;
fc
::
mutex
scp_open_mutex
;
fc
::
string
uname
;
fc
::
string
upass
;
fc
::
string
pubkey
;
fc
::
string
privkey
;
fc
::
string
passphrase
;
fc
::
string
hostname
;
uint16_t
port
;
bool
session_connected
;
fc
::
promise
<
boost
::
system
::
error_code
>::
ptr
read_prom
;
fc
::
promise
<
boost
::
system
::
error_code
>::
ptr
write_prom
;
fc
::
spin_lock
_spin_lock
;
int
_trace_level
;
logger
logr
;
bool
remote_system_is_windows
;
// true if windows, false if unix, used for command-line quoting and maybe filename translation
LIBSSH2_CHANNEL
*
open_channel
(
const
fc
::
string
&
pty_type
);
static
void
kbd_callback
(
const
char
*
name
,
int
name_len
,
const
char
*
instruction
,
int
instruction_len
,
int
num_prompts
,
const
LIBSSH2_USERAUTH_KBDINT_PROMPT
*
prompts
,
LIBSSH2_USERAUTH_KBDINT_RESPONSE
*
responses
,
void
**
abstract
);
void
connect
();
static
void
handle_trace
(
LIBSSH2_SESSION
*
session
,
void
*
context
,
const
char
*
data
,
size_t
length
);
void
close
();
void
authenticate
();
bool
try_pass
();
bool
try_keyboard
();
bool
try_pub_key
();
// don't call this "unlocked" version directly
template
<
typename
T
>
int
call_ssh2_function_unlocked
(
const
T
&
lambda
,
bool
check_for_errors
=
true
);
// calls into libssh2, waits and retries the function if we get LIBSSH2_ERROR_EAGAIN
template
<
typename
T
>
int
call_ssh2_function
(
const
T
&
lambda
,
bool
check_for_errors
=
true
);
// calls into libssh2, waits and retries the function if we get LIBSSH2_ERROR_EAGAIN
// if libssh2 returns an error, get extended info and throw a message with ${code} and ${message}
// set appropriately.
template
<
typename
T
>
int
call_ssh2_function_throw
(
const
T
&
lambda
,
const
char
*
message
=
"libssh2 call failed ${code} - ${message}"
,
bool
check_for_errors
=
true
);
// this version is a little different, it handles functions like libssh2_sftp_init which return
// a pointer instead of an int. These retry automatically if the result is NULL and the error
// is LIBSSH2_ERROR_EAGAIN
template
<
typename
return_type
>
return_type
call_ssh2_ptr_function_throw
(
std
::
function
<
return_type
()
>
lambda
,
const
char
*
message
=
"libssh2 call failed ${code} - ${message}"
,
bool
check_for_errors
=
true
);
void
wait_on_socket
(
int
additionalDirections
=
0
);
void
init_sftp
();
};
// #define OLD_BLOCKING,
// the OLD_BLOCKING version of these functions will ensure that if a libssh2 function returns
// LIBSSH2_ERROR_EAGAIN, no other libssh2 functions will be called until that function has been
// called again and returned some other value.
//
// if you don't define this and use the new version of this, we will release the lock and let
// other libssh2 functions be called *unless* it appears that there was unwritten data.
//
// the OLD_BLOCKING version is too conservative -- if you try to read on a channel that doesn't
// have any data, you're likely to deadlock. The new version is not heavily tested and may be
// too lax, time will tell.
#ifdef OLD_BLOCKING
// don't call this "unlocked" version directly
template
<
typename
T
>
int
client_impl
::
call_ssh2_function_unlocked
(
const
T
&
lambda
,
bool
check_for_errors
/* = true */
)
{
int
ec
=
lambda
();
while
(
ec
==
LIBSSH2_ERROR_EAGAIN
)
{
wait_on_socket
();
ec
=
lambda
();
}
// this assert catches bugs in libssh2 if libssh2 returns ec != LIBSSH2_ERROR_EAGAIN
// but the internal session data indicates a data write is still in progress
// set check_for_errors to false when closing the connection
assert
(
!
check_for_errors
||
!
session
->
packet
.
olen
);
return
ec
;
}
// calls into libssh2, waits and retries the function if we get LIBSSH2_ERROR_EAGAIN
template
<
typename
T
>
int
client_impl
::
call_ssh2_function
(
const
T
&
lambda
,
bool
check_for_errors
/* = true */
)
{
fc
::
scoped_lock
<
fc
::
mutex
>
lock
(
ssh_session_mutex
);
return
call_ssh2_function_unlocked
(
lambda
,
check_for_errors
);
}
#else
// calls into libssh2, waits and retries the function if we get LIBSSH2_ERROR_EAGAIN
template
<
typename
T
>
int
client_impl
::
call_ssh2_function
(
const
T
&
lambda
,
bool
check_for_errors
/* = true */
)
{
fc
::
unique_lock
<
fc
::
mutex
>
lock
(
ssh_session_mutex
);
int
ec
=
lambda
();
while
(
ec
==
LIBSSH2_ERROR_EAGAIN
)
{
bool
unlock_to_wait
=
!
session
->
packet
.
olen
;
if
(
unlock_to_wait
)
lock
.
unlock
();
wait_on_socket
();
if
(
unlock_to_wait
)
lock
.
lock
();
ec
=
lambda
();
}
// this assert catches bugs in libssh2 if libssh2 returns ec != LIBSSH2_ERROR_EAGAIN
// but the internal session data indicates a data write is still in progress
// set check_for_errors to false when closing the connection
assert
(
!
check_for_errors
||
!
session
->
packet
.
olen
);
return
ec
;
}
#endif
#ifdef OLD_BLOCKING
// calls into libssh2, waits and retries the function if we get LIBSSH2_ERROR_EAGAIN
// if libssh2 returns an error, get extended info and throw a message with ${code} and ${message}
// set appropriately.
template
<
typename
T
>
int
client_impl
::
call_ssh2_function_throw
(
const
T
&
lambda
,
const
char
*
message
/* = "libssh2 call failed ${code} - ${message}" */
,
bool
check_for_errors
/* = true */
)
{
fc
::
scoped_lock
<
fc
::
mutex
>
lock
(
ssh_session_mutex
);
int
ec
=
call_ssh2_function_unlocked
(
lambda
,
check_for_errors
);
if
(
ec
==
LIBSSH2_ERROR_SFTP_PROTOCOL
&&
sftp
)
{
ec
=
libssh2_sftp_last_error
(
sftp
);
FC_THROW
(
message
,
(
"code"
,
ec
).
set
(
"message"
,
"SFTP protocol error"
));
}
else
if
(
ec
<
0
)
{
char
*
msg
=
0
;
ec
=
libssh2_session_last_error
(
session
,
&
msg
,
0
,
0
);
FC_THROW
(
message
,
(
"code"
,
ec
).
set
(
"message"
,
msg
));
}
return
ec
;
}
#else
// calls into libssh2, waits and retries the function if we get LIBSSH2_ERROR_EAGAIN
// if libssh2 returns an error, get extended info and throw a message with ${code} and ${message}
// set appropriately.
template
<
typename
T
>
int
client_impl
::
call_ssh2_function_throw
(
const
T
&
lambda
,
const
char
*
message
/* = "libssh2 call failed ${code} - ${message}" */
,
bool
check_for_errors
/* = true */
)
{
fc
::
unique_lock
<
fc
::
mutex
>
lock
(
ssh_session_mutex
);
int
ec
=
lambda
();
while
(
ec
==
LIBSSH2_ERROR_EAGAIN
)
{
bool
unlock_to_wait
=
!
session
->
packet
.
olen
;
if
(
unlock_to_wait
)
lock
.
unlock
();
wait_on_socket
();
if
(
unlock_to_wait
)
lock
.
lock
();
ec
=
lambda
();
}
// this assert catches bugs in libssh2 if libssh2 returns ec != LIBSSH2_ERROR_EAGAIN
// but the internal session data indicates a data write is still in progress
// set check_for_errors to false when closing the connection
assert
(
!
check_for_errors
||
!
session
->
packet
.
olen
);
if
(
ec
==
LIBSSH2_ERROR_SFTP_PROTOCOL
&&
sftp
)
{
ec
=
libssh2_sftp_last_error
(
sftp
);
FC_THROW
(
message
,
(
"code"
,
ec
).
set
(
"message"
,
"SFTP protocol error"
));
}
else
if
(
ec
<
0
)
{
char
*
msg
=
0
;
ec
=
libssh2_session_last_error
(
session
,
&
msg
,
0
,
0
);
FC_THROW
(
message
,
(
"code"
,
ec
).
set
(
"message"
,
msg
));
}
return
ec
;
}
#endif
#ifdef OLD_BLOCKING
// this version is a little different, it handles functions like libssh2_sftp_init which return
// a pointer instead of an int. These retry automatically if the result is NULL and the error
// is LIBSSH2_ERROR_EAGAIN
template
<
typename
return_type
>
return_type
client_impl
::
call_ssh2_ptr_function_throw
(
std
::
function
<
return_type
()
>
lambda
,
const
char
*
message
/* = "libssh2 call failed ${code} - ${message}" */
,
bool
check_for_errors
/* = true */
)
{
fc
::
scoped_lock
<
fc
::
mutex
>
lock
(
ssh_session_mutex
);
return_type
ret
=
lambda
();
while
(
!
ret
)
{
char
*
msg
=
0
;
int
ec
=
libssh2_session_last_error
(
session
,
&
msg
,
NULL
,
0
);
if
(
ec
==
LIBSSH2_ERROR_EAGAIN
)
{
wait_on_socket
();
ret
=
lambda
();
}
else
if
(
ec
==
LIBSSH2_ERROR_SFTP_PROTOCOL
&&
sftp
)
{
ec
=
libssh2_sftp_last_error
(
sftp
);
FC_THROW
(
message
,
(
"code"
,
ec
).
set
(
"message"
,
"SFTP protocol error"
));
}
else
{
ec
=
libssh2_session_last_error
(
session
,
&
msg
,
0
,
0
);
FC_THROW
(
message
,
(
"code"
,
ec
).
set
(
"message"
,
msg
));
}
}
assert
(
!
check_for_errors
||
!
session
->
packet
.
olen
);
return
ret
;
}
#else
// this version is a little different, it handles functions like libssh2_sftp_init which return
// a pointer instead of an int. These retry automatically if the result is NULL and the error
// is LIBSSH2_ERROR_EAGAIN
template
<
typename
return_type
>
return_type
client_impl
::
call_ssh2_ptr_function_throw
(
std
::
function
<
return_type
()
>
lambda
,
const
char
*
message
/* = "libssh2 call failed ${code} - ${message}" */
,
bool
check_for_errors
/* = true */
)
{
fc
::
unique_lock
<
fc
::
mutex
>
lock
(
ssh_session_mutex
);
return_type
ret
=
lambda
();
while
(
!
ret
)
{
char
*
msg
=
0
;
int
ec
=
libssh2_session_last_error
(
session
,
&
msg
,
NULL
,
0
);
if
(
ec
==
LIBSSH2_ERROR_EAGAIN
)
{
bool
unlock_to_wait
=
!
session
->
packet
.
olen
;
if
(
unlock_to_wait
)
lock
.
unlock
();
wait_on_socket
();
if
(
unlock_to_wait
)
lock
.
lock
();
ret
=
lambda
();
}
else
if
(
ec
==
LIBSSH2_ERROR_SFTP_PROTOCOL
&&
sftp
)
{
ec
=
libssh2_sftp_last_error
(
sftp
);
FC_THROW
(
message
,
(
"code"
,
ec
).
set
(
"message"
,
"SFTP protocol error"
));
}
else
{
ec
=
libssh2_session_last_error
(
session
,
&
msg
,
0
,
0
);
FC_THROW
(
message
,
(
"code"
,
ec
).
set
(
"message"
,
msg
));
}
}
assert
(
!
check_for_errors
||
!
session
->
packet
.
olen
);
return
ret
;
}
#endif
}
}
}
libraries/fc/src/ssh/process.cpp
已删除
100644 → 0
浏览文件 @
c2f9a372
#define NOMINMAX // prevent windows from defining min and max macros
#include <libssh2.h>
#include <libssh2_sftp.h>
#include <boost/filesystem.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
#include <fc/ssh/client.hpp>
#include <fc/ssh/process.hpp>
#include <fc/io/sstream.hpp>
#include <fc/vector.hpp>
#include <fc/thread/unique_lock.hpp>
#include "client_impl.hpp"
#if defined (_MSC_VER)
#pragma warning (disable : 4355)
#endif
namespace
fc
{
namespace
ssh
{
namespace
detail
{
class
process_impl
;
class
process_istream
:
public
fc
::
istream
{
public:
process_istream
(
process_impl
&
p
,
int
c
)
:
proc
(
p
),
chan
(
c
){}
virtual
size_t
readsome
(
char
*
buf
,
size_t
len
);
virtual
bool
eof
()
const
;
process_impl
&
proc
;
int
chan
;
};
class
process_ostream
:
public
fc
::
ostream
{
public:
process_ostream
(
process_impl
&
p
)
:
proc
(
p
){}
virtual
size_t
writesome
(
const
char
*
buf
,
size_t
len
);
virtual
void
close
();
virtual
void
flush
();
process_impl
&
proc
;
};
class
process_impl
{
public:
process_impl
(
client_ptr
c
);
~
process_impl
();
//process_impl( const client& c, const fc::string& cmd, const fc::string& pty_type );
void
exec
(
const
fc
::
path
&
exe
,
vector
<
string
>
args
,
const
fc
::
path
&
work_dir
/* = fc::path() */
,
fc
::
iprocess
::
exec_opts
opts
/* = open_all */
);
int
read_some
(
char
*
data
,
size_t
len
,
int
stream_id
);
int
write_some
(
const
char
*
data
,
size_t
len
,
int
stream_id
);
void
flush
();
void
send_eof
();
LIBSSH2_CHANNEL
*
chan
;
client_ptr
sshc
;
buffered_ostream_ptr
buffered_std_in
;
buffered_istream_ptr
buffered_std_out
;
buffered_istream_ptr
buffered_std_err
;
fc
::
string
command
;
fc
::
promise
<
int
>::
ptr
result
;
fc
::
optional
<
int
>
return_code
;
fc
::
ostring
return_signal
;
fc
::
ostring
return_signal_message
;
private:
static
fc
::
string
windows_shell_escape
(
const
fc
::
string
&
str
);
static
fc
::
string
unix_shell_escape
(
const
fc
::
string
&
str
);
static
fc
::
string
windows_shell_escape_command
(
const
fc
::
path
&
exe
,
const
vector
<
string
>&
args
);
static
fc
::
string
unix_shell_escape_command
(
const
fc
::
path
&
exe
,
const
vector
<
string
>&
args
);
};
}
// end namespace detail
process
::
process
(
client_ptr
c
)
:
my
(
new
detail
::
process_impl
(
c
))
{}
process
::~
process
()
{}
iprocess
&
process
::
exec
(
const
fc
::
path
&
exe
,
vector
<
string
>
args
,
const
fc
::
path
&
work_dir
/* = fc::path() */
,
exec_opts
opts
/* = open_all */
)
{
my
->
exec
(
exe
,
args
,
work_dir
,
opts
);
return
*
this
;
}
/**
* Blocks until the result code of the process has been returned.
*/
int
process
::
result
()
{
if
(
!
my
->
return_code
&&
!
my
->
return_signal
)
{
// we don't have any cached exit status, so wait and obtain the values now
my
->
sshc
->
my
->
call_ssh2_function
(
boost
::
bind
(
libssh2_channel_wait_eof
,
my
->
chan
));
my
->
sshc
->
my
->
call_ssh2_function_throw
(
boost
::
bind
(
libssh2_channel_wait_closed
,
my
->
chan
),
"Error waiting on socket to close: ${message}"
);
char
*
exit_signal
;
char
*
error_message
;
libssh2_channel_get_exit_signal
(
my
->
chan
,
&
exit_signal
,
NULL
,
&
error_message
,
NULL
,
NULL
,
NULL
);
if
(
exit_signal
)
{
// process terminated with a signal
my
->
return_signal
=
exit_signal
;
libssh2_free
(
my
->
chan
->
session
,
exit_signal
);
if
(
error_message
)
{
my
->
return_signal_message
=
error_message
;
libssh2_free
(
my
->
chan
->
session
,
error_message
);
}
}
else
my
->
return_code
=
libssh2_channel_get_exit_status
(
my
->
chan
);
}
if
(
my
->
return_signal
)
FC_THROW
(
"process terminated with signal ${signal}: ${signal_message}"
,
(
"signal"
,
*
my
->
return_signal
)
(
"signal_message"
,
my
->
return_signal_message
?
*
my
->
return_signal_message
:
""
));
else
return
*
my
->
return_code
;
}
void
process
::
kill
()
{
elog
(
"error: fc::ssh::process::kill() not supported"
);
}
/**
* @brief returns a stream that writes to the procss' stdin
*/
fc
::
buffered_ostream_ptr
process
::
in_stream
()
{
return
my
->
buffered_std_in
;
}
/**
* @brief returns a stream that reads from the process' stdout
*/
fc
::
buffered_istream_ptr
process
::
out_stream
()
{
return
my
->
buffered_std_out
;
}
/**
* @brief returns a stream that reads from the process' stderr
*/
fc
::
buffered_istream_ptr
process
::
err_stream
()
{
return
my
->
buffered_std_err
;
}
void
detail
::
process_impl
::
flush
()
{
if
(
!
chan
)
return
;
/* channel_flush deleates input buffer, and does not ensure writes go out
*
int ec = libssh2_channel_flush_ex( chan, LIBSSH2_CHANNEL_FLUSH_EXTENDED_DATA);
while( ec == LIBSSH2_ERROR_EAGAIN ) {
sshc.my->wait_on_socket();
ec = libssh2_channel_flush_ex( chan, LIBSSH2_CHANNEL_FLUSH_EXTENDED_DATA );
}
ec = libssh2_channel_flush( chan );
while( ec == LIBSSH2_ERROR_EAGAIN ) {
sshc.my->wait_on_socket();
ec = libssh2_channel_flush( chan );
}
if( ec < 0 ) {
FC_THROW( "ssh flush failed", ( "channel_error", ec) );
}
*/
}
int
detail
::
process_impl
::
read_some
(
char
*
data
,
size_t
len
,
int
stream_id
){
if
(
!
sshc
->
my
->
session
)
{
FC_THROW
(
"Session closed"
);
}
int
rc
;
char
*
buf
=
data
;
size_t
buflen
=
len
;
do
{
rc
=
sshc
->
my
->
call_ssh2_function_throw
(
boost
::
bind
(
libssh2_channel_read_ex
,
chan
,
stream_id
,
buf
,
buflen
),
"read failed: ${message}"
);
if
(
rc
>
0
)
{
buf
+=
rc
;
buflen
-=
rc
;
return
buf
-
data
;
}
else
if
(
rc
==
0
)
{
if
(
libssh2_channel_eof
(
chan
)
)
return
-
1
;
// eof
sshc
->
my
->
wait_on_socket
();
}
}
while
(
rc
>=
0
&&
buflen
);
return
buf
-
data
;
}
int
detail
::
process_impl
::
write_some
(
const
char
*
data
,
size_t
len
,
int
stream_id
)
{
if
(
!
sshc
->
my
->
session
)
{
FC_THROW
(
"Session closed"
);
}
int
rc
;
const
char
*
buf
=
data
;
size_t
buflen
=
len
;
do
{
rc
=
sshc
->
my
->
call_ssh2_function_throw
(
boost
::
bind
(
libssh2_channel_write_ex
,
chan
,
stream_id
,
buf
,
buflen
),
"write failed: ${message}"
);
if
(
rc
>
0
)
{
buf
+=
rc
;
buflen
-=
rc
;
return
buf
-
data
;
}
else
if
(
rc
==
0
)
{
if
(
libssh2_channel_eof
(
chan
)
)
{
FC_THROW
(
"EOF"
);
//return -1; // eof
}
}
}
while
(
rc
>=
0
&&
buflen
);
return
buf
-
data
;
}
void
detail
::
process_impl
::
send_eof
()
{
if
(
sshc
->
my
->
session
)
sshc
->
my
->
call_ssh2_function_throw
(
boost
::
bind
(
libssh2_channel_send_eof
,
chan
),
"send eof failed: ${message}"
);
}
size_t
detail
::
process_istream
::
readsome
(
char
*
buf
,
size_t
len
)
{
int
bytesRead
=
proc
.
read_some
(
buf
,
len
,
chan
);
if
(
bytesRead
<
0
)
FC_THROW
(
"EOF"
);
else
return
bytesRead
;
}
bool
detail
::
process_istream
::
eof
()
const
{
return
0
!=
libssh2_channel_eof
(
proc
.
chan
);
}
size_t
detail
::
process_ostream
::
writesome
(
const
char
*
buf
,
size_t
len
)
{
return
proc
.
write_some
(
buf
,
len
,
0
);
}
void
detail
::
process_ostream
::
close
(){
proc
.
send_eof
();
}
void
detail
::
process_ostream
::
flush
(){
proc
.
flush
();
}
detail
::
process_impl
::
process_impl
(
client_ptr
c
)
:
chan
(
nullptr
),
sshc
(
c
),
buffered_std_in
(
new
buffered_ostream
(
ostream_ptr
(
new
process_ostream
(
*
this
)))),
buffered_std_out
(
new
buffered_istream
(
istream_ptr
(
new
process_istream
(
*
this
,
0
)))),
buffered_std_err
(
new
buffered_istream
(
istream_ptr
(
new
process_istream
(
*
this
,
SSH_EXTENDED_DATA_STDERR
))))
{
}
detail
::
process_impl
::~
process_impl
()
{
if
(
chan
)
{
sshc
->
my
->
call_ssh2_function
(
boost
::
bind
(
libssh2_channel_free
,
chan
));
chan
=
NULL
;
}
}
// these rules work pretty well for a standard bash shell on unix
fc
::
string
detail
::
process_impl
::
unix_shell_escape
(
const
fc
::
string
&
str
)
{
if
(
str
.
find_first_of
(
" ;&|><*?`$(){}[]!#'
\"
"
)
==
fc
::
string
::
npos
)
return
str
;
fc
::
string
escaped_quotes
(
str
);
for
(
size_t
start
=
escaped_quotes
.
find
(
"'"
);
start
!=
fc
::
string
::
npos
;
start
=
escaped_quotes
.
find
(
"'"
,
start
+
5
))
escaped_quotes
.
replace
(
start
,
1
,
"'
\"
'
\"
'"
);
fc
::
string
escaped_str
(
"
\'
"
);
escaped_str
+=
escaped_quotes
;
escaped_str
+=
"
\'
"
;
return
escaped_str
;
}
fc
::
string
detail
::
process_impl
::
unix_shell_escape_command
(
const
fc
::
path
&
exe
,
const
vector
<
string
>&
args
)
{
fc
::
stringstream
command_line
;
command_line
<<
unix_shell_escape
(
exe
.
string
());
for
(
unsigned
i
=
0
;
i
<
args
.
size
();
++
i
)
command_line
<<
" "
<<
unix_shell_escape
(
args
[
i
]);
return
command_line
.
str
();
}
// windows command-line escaping rules are a disaster, partly because how the command-line is
// parsed depends on what program you're running. In windows, the command line is passed in
// as a single string, and the process is left to interpret it as it sees fit. The standard
// C runtime uses one set of rules, the function CommandLineToArgvW usually used by
// GUI-mode programs uses a different set.
// Here we try to find a common denominator that works well for simple cases
// it's only minimally tested right now due to time constraints.
fc
::
string
detail
::
process_impl
::
windows_shell_escape
(
const
fc
::
string
&
str
)
{
if
(
str
.
find_first_of
(
"
\"
"
)
==
fc
::
string
::
npos
)
return
str
;
fc
::
string
escaped_quotes
(
str
);
for
(
size_t
start
=
escaped_quotes
.
find
(
"
\"
"
);
start
!=
fc
::
string
::
npos
;
start
=
escaped_quotes
.
find
(
"
\"
"
,
start
+
2
))
escaped_quotes
.
replace
(
start
,
1
,
"
\\\"
"
);
fc
::
string
escaped_str
(
"
\"
"
);
escaped_str
+=
escaped_quotes
;
escaped_str
+=
"
\"
"
;
return
escaped_str
;
}
fc
::
string
detail
::
process_impl
::
windows_shell_escape_command
(
const
fc
::
path
&
exe
,
const
vector
<
string
>&
args
)
{
fc
::
stringstream
command_line
;
command_line
<<
windows_shell_escape
(
exe
.
string
());
for
(
unsigned
i
=
0
;
i
<
args
.
size
();
++
i
)
command_line
<<
" "
<<
windows_shell_escape
(
args
[
i
]);
return
command_line
.
str
();
}
void
detail
::
process_impl
::
exec
(
const
fc
::
path
&
exe
,
vector
<
string
>
args
,
const
fc
::
path
&
work_dir
/* = fc::path() */
,
fc
::
iprocess
::
exec_opts
opts
/* = open_all */
)
{
chan
=
sshc
->
my
->
open_channel
(
""
);
sshc
->
my
->
call_ssh2_function
(
boost
::
bind
(
libssh2_channel_handle_extended_data2
,
chan
,
LIBSSH2_CHANNEL_EXTENDED_DATA_NORMAL
));
try
{
fc
::
scoped_lock
<
fc
::
mutex
>
process_startup_lock
(
sshc
->
my
->
process_startup_mutex
);
fc
::
string
command_line
=
sshc
->
my
->
remote_system_is_windows
?
windows_shell_escape_command
(
exe
,
args
)
:
unix_shell_escape_command
(
exe
,
args
);
sshc
->
my
->
call_ssh2_function_throw
(
boost
::
bind
(
libssh2_channel_process_startup
,
chan
,
"exec"
,
sizeof
(
"exec"
)
-
1
,
command_line
.
c_str
(),
command_line
.
size
()),
"exec failed: ${message}"
);
// equiv to libssh2_channel_exec(chan, cmd) macro
}
catch
(
fc
::
exception
&
er
)
{
elog
(
"error starting process"
);
FC_RETHROW_EXCEPTION
(
er
,
error
,
"error starting process"
);
}
}
}
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录