45 #include <sys/ioctl.h>
50 #define SSH_READ_PAGE_SIZE 8192
52 #define LOG_PREFIX "[" << m_hostname << "] === SshSocketHandler.cc:" << __LINE__ << " === "
58 STATE_CREATE_SESSION = 1,
59 STATE_COMPLETE_CONNECTION = 2,
60 STATE_VERIFY_KNOWNHOST = 3,
61 STATE_AUTHENTICATE = 4,
63 STATE_COMPLETE_CHANNEL_SESSION_OPEN = 6,
64 STATE_CHANNEL_REQUEST_EXEC = 7,
65 STATE_CHANNEL_REQUEST_READ = 8
68 const char *state_str(
int state) {
72 case (STATE_COMPLETE_CONNECTION):
73 return "COMPLETE_CONNECTION";
74 case (STATE_CREATE_SESSION):
75 return "CREATE_SESSION";
76 case (STATE_VERIFY_KNOWNHOST):
77 return "VERIFY_KNOWNHOST";
78 case (STATE_AUTHENTICATE):
79 return "AUTHENTICATE";
80 case (STATE_CONNECTED):
82 case (STATE_COMPLETE_CHANNEL_SESSION_OPEN):
83 return "STATE_COMPLETE_CHANNEL_SESSION_OPEN";
84 case (STATE_CHANNEL_REQUEST_EXEC):
85 return "STATE_CHANNEL_REQUEST_EXEC";
86 case (STATE_CHANNEL_REQUEST_READ):
87 return "STATE_CHANNEL_REQUEST_READ";
94 void log_callback_function(ssh_session session,
int priority,
95 const char *message,
void *userdata) {
99 int auth_callback_function(
const char *prompt,
char *buf,
size_t len,
100 int echo,
int verify,
void *userdata) {
101 return ((
SshSocketHandler *)userdata)->auth_callback(prompt, buf, len, echo, verify);
104 void connect_status_callback_function(
void *userdata,
float status) {
108 void global_request_callback_function(ssh_session session,
109 ssh_message message,
void *userdata) {
113 void exit_status_callback_function(ssh_session session,
128 if (!strcasecmp(value.c_str(),
"none"))
129 ms_libssh_verbosity = SSH_LOG_NOLOG;
130 else if (!strcasecmp(value.c_str(),
"warning"))
131 ms_libssh_verbosity = SSH_LOG_WARNING;
132 else if (!strcasecmp(value.c_str(),
"protocol"))
133 ms_libssh_verbosity = SSH_LOG_PROTOCOL;
134 else if (!strcasecmp(value.c_str(),
"packet"))
135 ms_libssh_verbosity = SSH_LOG_PACKET;
136 else if (!strcasecmp(value.c_str(),
"functions"))
137 ms_libssh_verbosity = SSH_LOG_FUNCTIONS;
139 cout <<
"Unrecognized libssh logging level: " << value << endl;
140 quick_exit(EXIT_FAILURE);
145 : m_hostname(hostname), m_log_collector(1024),
148 m_sd = socket(AF_INET, SOCK_STREAM, 0);
150 m_error = string(
"socket(AF_INET, SOCK_STREAM, 0) fialed - ") + strerror(errno);
157 struct hostent *server = gethostbyname(
m_hostname.c_str());
158 if (server ==
nullptr) {
159 m_error = string(
"gethostbyname('") +
m_hostname +
"') failed - " + hstrerror(h_errno);
164 struct sockaddr_in serv_addr;
165 bzero((
char *) &serv_addr,
sizeof(serv_addr));
166 serv_addr.sin_family = AF_INET;
167 bcopy((
char *)server->h_addr,
168 (
char *)&serv_addr.sin_addr.s_addr,
170 serv_addr.sin_port = htons(22);
178 m_state = STATE_CREATE_SESSION;
180 while (connect(
m_sd, (
struct sockaddr *)&serv_addr,
sizeof(serv_addr)) < 0) {
181 if (errno == EINTR) {
182 this_thread::sleep_for(chrono::milliseconds(1000));
185 else if (errno != EINPROGRESS) {
210 lock_guard<mutex> lock(
m_mutex);
214 cerr <<
LOG_PREFIX <<
"Entering handler (events="
221 case (STATE_INITIAL):
224 socklen_t sockerr_len =
sizeof(sockerr);
225 if (getsockopt(
m_sd, SOL_SOCKET, SO_ERROR, &sockerr, &sockerr_len) < 0) {
226 m_error = string(
"getsockopt(SO_ERROR) failed (") + strerror(errno) +
")";
231 m_error = string(
"connect() completion error (") + strerror(errno) +
")";
237 m_state = STATE_CREATE_SESSION;
239 case (STATE_CREATE_SESSION):
245 char *home = getenv(
"HOME");
247 HT_FATAL(
"Environment variable HOME is not set");
248 string ssh_dir(home);
249 ssh_dir.append(
"/.ssh");
250 ssh_options_set(
m_ssh_session, SSH_OPTIONS_SSH_DIR, ssh_dir.c_str());
253 ssh_options_set(
m_ssh_session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
264 m_callbacks.auth_function = auth_callback_function;
266 m_callbacks.connect_status_function = connect_status_callback_function;
267 m_callbacks.global_request_function = global_request_callback_function;
271 if (rc == SSH_ERROR) {
279 ssh_options_parse_config(
m_ssh_session,
"/etc/ssh/ssh_config");
282 string config_file = ssh_dir +
"/config";
284 ssh_options_parse_config(
m_ssh_session, config_file.c_str());
288 m_state = STATE_VERIFY_KNOWNHOST;
291 if (rc == SSH_ERROR) {
296 else if (rc == SSH_AGAIN) {
297 m_state = STATE_COMPLETE_CONNECTION;
302 case (STATE_COMPLETE_CONNECTION):
306 else if (rc == SSH_ERROR) {
312 m_state = STATE_VERIFY_KNOWNHOST;
314 case (STATE_VERIFY_KNOWNHOST):
321 case (STATE_AUTHENTICATE):
322 rc = ssh_userauth_publickey_auto(
m_ssh_session,
nullptr,
nullptr);
323 if (rc == SSH_AUTH_ERROR) {
328 else if (rc == SSH_AUTH_AGAIN) {
334 else if (rc == SSH_AUTH_DENIED) {
335 m_error = string(
"publickey authentication denied");
346 case (STATE_COMPLETE_CHANNEL_SESSION_OPEN):
347 rc = ssh_channel_open_session(
m_channel);
348 if (rc == SSH_AGAIN) {
353 else if (rc == SSH_ERROR) {
361 m_state = STATE_CHANNEL_REQUEST_EXEC;
363 case (STATE_CHANNEL_REQUEST_EXEC):
366 if (rc == SSH_AGAIN) {
371 else if (rc == SSH_ERROR) {
380 m_state = STATE_CHANNEL_REQUEST_READ;
382 case (STATE_CHANNEL_REQUEST_READ):
394 if (nbytes == SSH_ERROR) {
402 else if (nbytes == SSH_EOF) {
406 else if (nbytes <= 0)
432 if (nbytes == SSH_ERROR) {
440 else if (nbytes == SSH_EOF) {
444 else if (nbytes <= 0)
465 cerr <<
LOG_PREFIX <<
"At EOF (exit_status=" << exit_status
466 <<
", status_is_set="
487 case (STATE_CONNECTED):
497 cerr <<
LOG_PREFIX <<
"Leaving handler (poll_interest="
499 <<
", state=" << state_str(
m_state) <<
")\n";
513 cerr <<
"[" <<
m_hostname <<
"] " << message <<
"\n";
528 len = strlen(message);
541 cerr <<
LOG_PREFIX <<
"auth_callback (" << prompt <<
", buflen=" << len
542 <<
", echo=" << echo <<
", verify=" << verify <<
")\n";
548 cerr <<
LOG_PREFIX <<
"connect_status_callback " << (int)(status*100.0) <<
"%\n";
553 cerr <<
LOG_PREFIX <<
"global_request_callback (type=" << ssh_message_type(message)
554 <<
", subtype=" << ssh_message_subtype(message) <<
")\n";
564 unique_lock<mutex> lock(
m_mutex);
566 if (
m_cond.wait_until(lock, deadline) == cv_status::timeout) {
575 lock_guard<mutex> lock(
m_mutex);
593 if (rc == SSH_ERROR) {
599 rc = ssh_channel_open_session(
m_channel);
600 if (rc == SSH_AGAIN) {
601 m_state = STATE_COMPLETE_CHANNEL_SESSION_OPEN;
607 else if (rc == SSH_ERROR) {
618 m_state = STATE_CHANNEL_REQUEST_EXEC;
624 unique_lock<mutex> lock(
m_mutex);
633 unique_lock<mutex> lock(
m_mutex);
649 unique_lock<mutex> lock(
m_mutex);
670 if (!m_stdout_collector.last_line_is_partial())
692 if (!m_stderr_collector.last_line_is_partial())
707 out <<
"[" <<
m_hostname <<
"] " << line <<
"\n";
719 unsigned char *hash {};
732 rc = ssh_get_publickey_hash(key, SSH_PUBLICKEY_HASH_SHA1, &hash, &hlen);
735 m_error =
"problem computing public key hash";
743 case SSH_SERVER_KNOWN_OK:
746 case SSH_SERVER_KNOWN_CHANGED:
747 m_error =
"host key has changed";
751 case SSH_SERVER_FOUND_OTHER:
752 m_error =
"Key mis-match with one in known_hosts";
756 case SSH_SERVER_FILE_NOT_FOUND:
757 case SSH_SERVER_NOT_KNOWN:
760 m_error =
"problem writing known hosts file";
766 case SSH_SERVER_ERROR:
776 const char *base = output;
777 const char *end = output + len;
784 for (ptr = base; ptr<end; ptr++) {
790 cout << string(base, ptr-base) << endl;
795 cout << string(base, ptr-base);
805 const char *base = output;
806 const char *end = output + len;
813 for (ptr = base; ptr<end; ptr++) {
819 cerr << string(base, ptr-base) << endl;
824 cerr << string(base, ptr-base);
835 ioctl(
m_sd, FIONREAD, &count);
static Comm * instance()
Creates/returns singleton instance of the Comm class.
void set_exit_status(int exit_status)
libssh exit status callback This function sets m_command_exit_status to exit_status, sets m_command_exit_status_is_set to true, and then signals m_cond.
void connect_status_callback(float status)
libssh connection status callback.
int register_socket(int sd, const CommAddress &addr, RawSocketHandler *handler)
Registers an externally managed socket with comm event loop.
std::condition_variable m_cond
Condition variable signalling connection and command completion.
virtual ~SshSocketHandler()
Destructor.
void write_to_stderr(const char *output, size_t len)
Writes output to stderr Writes output to stderr, prefixing each line with '[' hostname ']'...
SshOutputCollector::Buffer m_stderr_buffer
Current stderr output buffer.
std::string m_command
Current command being issued.
SshOutputCollector m_stderr_collector
Output collector for stderr.
bool m_line_prefix_needed_stderr
Line prefix needs to be emitted on next stderr output.
int m_sd
Socket descriptor.
bool issue_command(const std::string &command)
Asynchronously issues a command.
SshOutputCollector m_stdout_collector
Output collector for stdout.
SshOutputCollector m_log_collector
Output collector for logging.
size_t remain() const
Returns amount of unused space remaining in buffer.
static bool exists(const String &fname)
Checks if a file or directory exists.
SshOutputCollector::Buffer m_stdout_buffer
Current stdout output buffer.
int auth_callback(const char *prompt, char *buf, size_t len, int echo, int verify)
libssh authorization callback.
std::mutex m_mutex
Mutex for serialzing access to members
int m_state
Current handler state.
SshSocketHandler(const std::string &hostname)
Constructor.
std::string m_error
Error message
bool m_line_prefix_needed_stdout
Line prefix needs to be emitted on next stdout output.
bool wait_for_connection(std::chrono::system_clock::time_point deadline)
Waits for connection establishment.
File system utility functions.
static int ms_libssh_verbosity
Libssh logging verbosity level.
std::string to_string(int events)
Returns a string representation of polling events.
#define SSH_READ_PAGE_SIZE
virtual bool handle(int sd, int events)
Socket event handler function.
bool status(ContextPtr &context, Timer &timer, Status &status)
Runs a status check on the master.
void add(Buffer buf)
Adds filled buffer to collector.
Fixed-size buffer to hold a portion of output.
bool m_channel_is_eof
Flag indicating that current channel is EOF.
Comm * m_comm
Pointer to comm layer.
Logging routines and macros.
virtual void deregister(int sd)
Deregisters socket.
Compatibility Macros for C/C++.
bool verify_knownhost()
Verifies host with public key method.
static void set_libssh_verbosity(const std::string &value)
Sets libssh logging verbosity level.
ssh_channel m_channel
libssh channel object
char * base
Pointer to beginning of buffer memory.
Declarations for PollEvent.
std::string m_hostname
Name of host to connect to.
ssh_callbacks_struct m_callbacks
libssh callbacks
String format(int sep= ':') const
Returns a string with a dotted notation ("127.0.0.1:8080") including the port.
static bool set_flags(int fd, int flags)
Sets fcntl flags of a socket.
int m_poll_interest
Current polling interest.
bool m_command_exit_status_is_set
Flag indicating that the exit status has been set.
#define HT_FATALF(msg,...)
Writing can be performed without blocking.
void cancel()
Cancels outstanding connection establishment or command execution.
bool m_terminal_output
Redirect output to terminal.
void add(const char *data, size_t len)
Adds data to buffer.
static void enable_debug()
Enables debug logging output.
char * ptr
Pointer to next unused position in buffer memory.
bool m_cancelled
Flag indicating that outstanding operations should be cancelled.
SshOutputCollector::Buffer m_log_buffer
Current logging output buffer.
Raw socket handler for ssh protocol driver.
void log_callback(ssh_session session, int priority, const char *message)
Writes log messages to logging output collector.
bool empty() const
Returns true if no output has been collected Returns true if there are no collected buffers or if non...
CommAddress m_comm_address
Address of connection.
int m_command_exit_status
Command exit status.
size_t fill() const
Returns amount of buffer filled.
void global_request_callback(ssh_session session, ssh_message message)
libssh global request callback.
void write_to_stdout(const char *output, size_t len)
Writes output to stdout Writes output to stdout, prefixing each line with '[' hostname ']'...
Buffer allocate_buffer()
Allocate a buffer.
bool wait_for_command_completion()
Waits for command completion.
void dump_log(std::ostream &out)
Writes collected log messages to output stream.
Declarations for SshSocketHandler.
ssh_session m_ssh_session
libssh sesison object
Address abstraction to hold either proxy name or IPv4:port address.
static bool ms_debug_enabled
Flag for enabling debugging output.
bool socket_has_data()
Determines if data available on socket for reading Checks socket descriptor m_sd to see if there is a...
ssh_channel_callbacks_struct m_channel_callbacks
libssh channel callbacks
void set_terminal_output(bool val)
Tells handler to send collected output subsequent output to terminal If val is true, sends any collected stdout or stderr output to terminal and sets m_terminal_output to true which causes any subsequent output collected to be sent to the terminal.