SSH subagent initial implementation
authorVictor Kirhenshtein <victor@netxms.org>
Mon, 1 Aug 2016 09:16:09 +0000 (12:16 +0300)
committerVictor Kirhenshtein <victor@netxms.org>
Mon, 1 Aug 2016 09:16:09 +0000 (12:16 +0300)
ChangeLog
configure.ac
src/agent/subagents/ssh/.cproject [new file with mode: 0644]
src/agent/subagents/ssh/.project [new file with mode: 0644]
src/agent/subagents/ssh/.settings/language.settings.xml [new file with mode: 0644]
src/agent/subagents/ssh/Makefile.am [new file with mode: 0644]
src/agent/subagents/ssh/handlers.cpp [new file with mode: 0644]
src/agent/subagents/ssh/main.cpp [new file with mode: 0644]
src/agent/subagents/ssh/session.cpp [new file with mode: 0644]
src/agent/subagents/ssh/ssh_subagent.h [new file with mode: 0644]

index 067d667..2a35d89 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -11,6 +11,7 @@
 - Max size of agent data collectiors pool can be configured
 - Agent data reconciliation block size and timeout can be configured
 - New agent parameters System.CPU.CurrentUsage and System.CPU.CurrentUsage(*)
+- SSH subagent (for collecting data and executing actions via SSH)
 - Management console
        - New editors for Agent Config Policy and Log Parser Policy. 
        - DCI summary tables with empty menu path not shown in object context menu
index 72dccde..b87d9bf 100644 (file)
@@ -1778,6 +1778,16 @@ if test "x$HAVE_LIBCURL" = "xyes"; then
   AC_DEFINE(HAVE_LIBCURL, 1, Define to 1 if libcurl is available)
 fi
 
+AC_CHECK_HEADER(libssh/libssh.h,HAVE_LIBSSH=yes,HAVE_LIBSSH=no)
+if test "x$HAVE_LIBSSH" = "xyes"; then
+  AC_CHECK_LIB(ssh, ssh_version, [ HAVE_LIBSSH=yes ], [ HAVE_LIBSSH=no ])
+fi
+
+if test "x$HAVE_LIBSSH" = "xyes"; then
+  SUBAGENT_DIRS="$SUBAGENT_DIRS ssh"
+  AC_DEFINE(HAVE_LIBSSH, 1, Define to 1 if libssh is available)
+fi
+
 if test "x$LDAP_SUPPORT" = "xyes"; then
        AC_CHECK_HEADER(ldap.h,HAVE_LDAP=yes,HAVE_LDAP=no)
        if test "x$HAVE_LDAP" = "xyes"; then
@@ -3206,6 +3216,7 @@ AC_CONFIG_FILES([
        src/agent/subagents/portCheck/Makefile
        src/agent/subagents/rpi/Makefile
        src/agent/subagents/sms/Makefile
+       src/agent/subagents/ssh/Makefile
        src/agent/subagents/sunos/Makefile
        src/agent/subagents/tuxedo/Makefile
        src/agent/subagents/mongodb/Makefile
@@ -3393,6 +3404,11 @@ if test "x${HAVE_LIBCRYPTO}" = "xyes"; then
 else
        echo "Encryption enabled      : NO"
 fi
+if test "x${HAVE_LIBSSH}" = "xyes"; then
+       echo "SSH support enabled     : YES"
+else
+       echo "SSH support enabled     : NO"
+fi
 if test "x${DB_DRIVERS}" != "x"; then
        echo "Build DB-Drivers        :${DB_DRIVERS}"
 else
diff --git a/src/agent/subagents/ssh/.cproject b/src/agent/subagents/ssh/.cproject
new file mode 100644 (file)
index 0000000..1a10181
--- /dev/null
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<?fileVersion 4.0.0?><cproject storage_type_id="org.eclipse.cdt.core.XmlProjectDescriptionStorage">
+       <storageModule moduleId="org.eclipse.cdt.core.settings">
+               <cconfiguration id="cdt.managedbuild.toolchain.gnu.base.1504455139">
+                       <storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="cdt.managedbuild.toolchain.gnu.base.1504455139" moduleId="org.eclipse.cdt.core.settings" name="Default">
+                               <externalSettings/>
+                               <extensions>
+                                       <extension id="org.eclipse.cdt.core.GNU_ELF" point="org.eclipse.cdt.core.BinaryParser"/>
+                                       <extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+                                       <extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+                                       <extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+                                       <extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/>
+                                       <extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+                               </extensions>
+                       </storageModule>
+                       <storageModule moduleId="cdtBuildSystem" version="4.0.0">
+                               <configuration artifactName="${ProjName}" buildProperties="" description="" id="cdt.managedbuild.toolchain.gnu.base.1504455139" name="Default" parent="org.eclipse.cdt.build.core.emptycfg">
+                                       <folderInfo id="cdt.managedbuild.toolchain.gnu.base.1504455139.289979902" name="/" resourcePath="">
+                                               <toolChain id="cdt.managedbuild.toolchain.gnu.base.1180412342" name="Linux GCC" superClass="cdt.managedbuild.toolchain.gnu.base">
+                                                       <targetPlatform archList="all" binaryParser="org.eclipse.cdt.core.GNU_ELF" id="cdt.managedbuild.target.gnu.platform.base.58229399" name="Debug Platform" osList="linux,hpux,aix,qnx" superClass="cdt.managedbuild.target.gnu.platform.base"/>
+                                                       <builder id="cdt.managedbuild.target.gnu.builder.base.1414682972" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Gnu Make Builder" superClass="cdt.managedbuild.target.gnu.builder.base"/>
+                                                       <tool id="cdt.managedbuild.tool.gnu.archiver.base.766164830" name="GCC Archiver" superClass="cdt.managedbuild.tool.gnu.archiver.base"/>
+                                                       <tool id="cdt.managedbuild.tool.gnu.cpp.compiler.base.491585394" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.base">
+                                                               <option id="gnu.cpp.compiler.option.include.paths.1403809153" name="Include paths (-I)" superClass="gnu.cpp.compiler.option.include.paths" useByScannerDiscovery="false" valueType="includePath">
+                                                                       <listOptionValue builtIn="false" value="&quot;${NETXMS_BASE}&quot;"/>
+                                                                       <listOptionValue builtIn="false" value="&quot;${NETXMS_BASE}/include&quot;"/>
+                                                               </option>
+                                                               <option id="gnu.cpp.compiler.option.preprocessor.def.1351105090" name="Defined symbols (-D)" superClass="gnu.cpp.compiler.option.preprocessor.def" useByScannerDiscovery="false" valueType="definedSymbols">
+                                                                       <listOptionValue builtIn="false" value="_THREAD_SAFE"/>
+                                                                       <listOptionValue builtIn="false" value="TRE_WCHAR=1"/>
+                                                                       <listOptionValue builtIn="false" value="UNICODE"/>
+                                                                       <listOptionValue builtIn="false" value="_GNU_SOURCE"/>
+                                                               </option>
+                                                               <inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.909715584" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
+                                                       </tool>
+                                                       <tool id="cdt.managedbuild.tool.gnu.c.compiler.base.191453070" name="GCC C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.base">
+                                                               <option id="gnu.c.compiler.option.include.paths.1563410816" name="Include paths (-I)" superClass="gnu.c.compiler.option.include.paths" useByScannerDiscovery="false" valueType="includePath">
+                                                                       <listOptionValue builtIn="false" value="&quot;${NETXMS_BASE}&quot;"/>
+                                                                       <listOptionValue builtIn="false" value="&quot;${NETXMS_BASE}/include&quot;"/>
+                                                               </option>
+                                                               <option id="gnu.c.compiler.option.preprocessor.def.symbols.1623929414" name="Defined symbols (-D)" superClass="gnu.c.compiler.option.preprocessor.def.symbols" useByScannerDiscovery="false" valueType="definedSymbols">
+                                                                       <listOptionValue builtIn="false" value="_THREAD_SAFE"/>
+                                                                       <listOptionValue builtIn="false" value="TRE_WCHAR=1"/>
+                                                                       <listOptionValue builtIn="false" value="UNICODE"/>
+                                                                       <listOptionValue builtIn="false" value="_GNU_SOURCE"/>
+                                                               </option>
+                                                               <inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.717761738" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
+                                                       </tool>
+                                                       <tool id="cdt.managedbuild.tool.gnu.c.linker.base.1850410162" name="GCC C Linker" superClass="cdt.managedbuild.tool.gnu.c.linker.base"/>
+                                                       <tool id="cdt.managedbuild.tool.gnu.cpp.linker.base.2141078905" name="GCC C++ Linker" superClass="cdt.managedbuild.tool.gnu.cpp.linker.base">
+                                                               <inputType id="cdt.managedbuild.tool.gnu.cpp.linker.input.697696890" superClass="cdt.managedbuild.tool.gnu.cpp.linker.input">
+                                                                       <additionalInput kind="additionalinputdependency" paths="$(USER_OBJS)"/>
+                                                                       <additionalInput kind="additionalinput" paths="$(LIBS)"/>
+                                                               </inputType>
+                                                       </tool>
+                                                       <tool id="cdt.managedbuild.tool.gnu.assembler.base.515252075" name="GCC Assembler" superClass="cdt.managedbuild.tool.gnu.assembler.base">
+                                                               <option id="gnu.both.asm.option.include.paths.118375124" name="Include paths (-I)" superClass="gnu.both.asm.option.include.paths" valueType="includePath">
+                                                                       <listOptionValue builtIn="false" value="&quot;${NETXMS_BASE}&quot;"/>
+                                                                       <listOptionValue builtIn="false" value="&quot;${NETXMS_BASE}/include&quot;"/>
+                                                               </option>
+                                                               <inputType id="cdt.managedbuild.tool.gnu.assembler.input.1667580872" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
+                                                       </tool>
+                                               </toolChain>
+                                       </folderInfo>
+                               </configuration>
+                       </storageModule>
+                       <storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
+               </cconfiguration>
+       </storageModule>
+       <storageModule moduleId="cdtBuildSystem" version="4.0.0">
+               <project id="ssh.null.790757896" name="ssh"/>
+       </storageModule>
+       <storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
+       <storageModule moduleId="scannerConfiguration">
+               <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
+               <scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.base.1504455139;cdt.managedbuild.toolchain.gnu.base.1504455139.289979902;cdt.managedbuild.tool.gnu.c.compiler.base.191453070;cdt.managedbuild.tool.gnu.c.compiler.input.717761738">
+                       <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
+               </scannerConfigBuildInfo>
+               <scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.base.1504455139;cdt.managedbuild.toolchain.gnu.base.1504455139.289979902;cdt.managedbuild.tool.gnu.cpp.compiler.base.491585394;cdt.managedbuild.tool.gnu.cpp.compiler.input.909715584">
+                       <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
+               </scannerConfigBuildInfo>
+       </storageModule>
+</cproject>
diff --git a/src/agent/subagents/ssh/.project b/src/agent/subagents/ssh/.project
new file mode 100644 (file)
index 0000000..6f45a71
--- /dev/null
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>ssh</name>
+       <comment></comment>
+       <projects>
+               <project>libnetxms</project>
+               <project>libnxagent</project>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name>
+                       <triggers>clean,full,incremental,</triggers>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name>
+                       <triggers>full,incremental,</triggers>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>org.eclipse.cdt.core.cnature</nature>
+               <nature>org.eclipse.cdt.core.ccnature</nature>
+               <nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature>
+               <nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature>
+       </natures>
+       <filteredResources>
+               <filter>
+                       <id>1469877111471</id>
+                       <name></name>
+                       <type>6</type>
+                       <matcher>
+                               <id>org.eclipse.ui.ide.multiFilter</id>
+                               <arguments>1.0-name-matches-false-false-*.o</arguments>
+                       </matcher>
+               </filter>
+               <filter>
+                       <id>1469877111481</id>
+                       <name></name>
+                       <type>6</type>
+                       <matcher>
+                               <id>org.eclipse.ui.ide.multiFilter</id>
+                               <arguments>1.0-name-matches-false-false-*.lo</arguments>
+                       </matcher>
+               </filter>
+               <filter>
+                       <id>1469877111488</id>
+                       <name></name>
+                       <type>6</type>
+                       <matcher>
+                               <id>org.eclipse.ui.ide.multiFilter</id>
+                               <arguments>1.0-name-matches-false-false-*.la</arguments>
+                       </matcher>
+               </filter>
+       </filteredResources>
+</projectDescription>
diff --git a/src/agent/subagents/ssh/.settings/language.settings.xml b/src/agent/subagents/ssh/.settings/language.settings.xml
new file mode 100644 (file)
index 0000000..f420acc
--- /dev/null
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<project>
+       <configuration id="cdt.managedbuild.toolchain.gnu.base.1504455139" name="Default">
+               <extension point="org.eclipse.cdt.core.LanguageSettingsProvider">
+                       <provider class="org.eclipse.cdt.core.language.settings.providers.LanguageSettingsGenericProvider" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider" name="CDT User Setting Entries" prefer-non-shared="true"/>
+                       <provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
+                       <provider copy-of="extension" id="org.eclipse.cdt.managedbuilder.core.GCCBuildCommandParser"/>
+                       <provider class="org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuiltinSpecsDetector" console="false" env-hash="1092372519076156371" id="org.eclipse.cdt.managedbuilder.core.GCCBuiltinSpecsDetector" keep-relative-paths="false" name="CDT GCC Built-in Compiler Settings" parameter="${COMMAND} ${FLAGS} -E -P -v -dD &quot;${INPUTS}&quot;" prefer-non-shared="true">
+                               <language-scope id="org.eclipse.cdt.core.gcc"/>
+                               <language-scope id="org.eclipse.cdt.core.g++"/>
+                       </provider>
+                       <provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
+               </extension>
+       </configuration>
+</project>
diff --git a/src/agent/subagents/ssh/Makefile.am b/src/agent/subagents/ssh/Makefile.am
new file mode 100644 (file)
index 0000000..27769a2
--- /dev/null
@@ -0,0 +1,18 @@
+SUBAGENT = ssh
+
+pkglib_LTLIBRARIES = ssh.la
+ssh_la_SOURCES = handlers.cpp main.cpp session.cpp
+ssh_la_CPPFLAGS=-I@top_srcdir@/include
+ssh_la_LDFLAGS = -module -avoid-version -export-symbols ../subagent.sym
+ssh_la_LIBADD = ../../libnxagent/libnxagent.la ../../../libnetxms/libnetxms.la -lssh
+
+EXTRA_DIST = ssh_subagent.h ssh.vcproj
+
+if !STATIC_BUILD
+install-exec-hook:
+       if test "x`uname -s`" = "xAIX" ; then OBJECT_MODE=@OBJECT_MODE@ $(AR) x $(DESTDIR)$(pkglibdir)/$(SUBAGENT).a $(DESTDIR)$(pkglibdir)/$(SUBAGENT)@SHLIB_SUFFIX@ ; rm -f $(DESTDIR)$(pkglibdir)/$(SUBAGENT).a ; fi
+       mv -f $(DESTDIR)$(pkglibdir)/$(SUBAGENT)@SHLIB_SUFFIX@ $(DESTDIR)$(pkglibdir)/$(SUBAGENT).nsm
+       rm -f $(DESTDIR)$(pkglibdir)/$(SUBAGENT).la
+       rm -f $(DESTDIR)$(libdir)/libnsm_$(SUBAGENT)@SHLIB_SUFFIX@
+       ln -s netxms/$(SUBAGENT).nsm $(DESTDIR)$(libdir)/libnsm_$(SUBAGENT)@SHLIB_SUFFIX@
+endif
diff --git a/src/agent/subagents/ssh/handlers.cpp b/src/agent/subagents/ssh/handlers.cpp
new file mode 100644 (file)
index 0000000..8dd8626
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+** NetXMS SSH subagent
+** Copyright (C) 2004-2016 Victor Kirhenshtein
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+** File: handlers.cpp
+**
+**/
+
+#include "ssh_subagent.h"
+
+/**
+ * Generic handler to execute command on any host
+ */
+LONG H_SSHCommand(const TCHAR *param, const TCHAR *arg, TCHAR *value, AbstractCommSession *session)
+{
+   TCHAR hostName[256], login[64], password[64], command[256];
+   if (!AgentGetParameterArg(param, 1, hostName, 256) ||
+       !AgentGetParameterArg(param, 2, login, 64) ||
+       !AgentGetParameterArg(param, 3, password, 64) ||
+       !AgentGetParameterArg(param, 4, command, 256))
+      return SYSINFO_RC_UNSUPPORTED;
+
+   UINT16 port = 22;
+   TCHAR *p = _tcschr(hostName, _T(':'));
+   if (p != NULL)
+   {
+      *p = 0;
+      p++;
+      port = (UINT16)_tcstoul(p, NULL, 10);
+   }
+
+   InetAddress addr = InetAddress::resolveHostName(hostName);
+   if (!addr.isValidUnicast())
+      return SYSINFO_RC_UNSUPPORTED;
+
+   LONG rc = SYSINFO_RC_ERROR;
+   SSHSession ssh(addr, port);
+   if (ssh.connect(login, password))
+   {
+      StringList *output = ssh.execute(command);
+      if (output != NULL)
+      {
+         if (output->size() > 0)
+         {
+            ret_string(value, output->get(0));
+            rc = SYSINFO_RC_SUCCESS;
+         }
+         delete output;
+      }
+      ssh.disconnect();
+   }
+   return rc;
+}
+
+/**
+ * Generic handler to execute command on any host (arguments as list
+ */
+LONG H_SSHCommandList(const TCHAR *param, const TCHAR *arg, StringList *value, AbstractCommSession *session)
+{
+   TCHAR hostName[256], login[64], password[64], command[256];
+   if (!AgentGetParameterArg(param, 1, hostName, 256) ||
+       !AgentGetParameterArg(param, 2, login, 64) ||
+       !AgentGetParameterArg(param, 3, password, 64) ||
+       !AgentGetParameterArg(param, 4, command, 256))
+      return SYSINFO_RC_UNSUPPORTED;
+
+   UINT16 port = 22;
+   TCHAR *p = _tcschr(hostName, _T(':'));
+   if (p != NULL)
+   {
+      *p = 0;
+      p++;
+      port = (UINT16)_tcstoul(p, NULL, 10);
+   }
+
+   InetAddress addr = InetAddress::resolveHostName(hostName);
+   if (!addr.isValidUnicast())
+      return SYSINFO_RC_UNSUPPORTED;
+
+   LONG rc = SYSINFO_RC_ERROR;
+   SSHSession ssh(addr, port);
+   if (ssh.connect(login, password))
+   {
+      StringList *output = ssh.execute(command);
+      if (output != NULL)
+      {
+         value->addAll(output);
+         rc = SYSINFO_RC_SUCCESS;
+         delete output;
+      }
+      ssh.disconnect();
+   }
+   return rc;
+}
diff --git a/src/agent/subagents/ssh/main.cpp b/src/agent/subagents/ssh/main.cpp
new file mode 100644 (file)
index 0000000..6530214
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+** NetXMS SSH subagent
+** Copyright (C) 2004-2016 Victor Kirhenshtein
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+** File: main.cpp
+**
+**/
+
+#include "ssh_subagent.h"
+
+/**
+ * Configuration file template
+ */
+static NX_CFG_TEMPLATE m_cfgTemplate[] =
+{
+       { _T(""), CT_END_OF_LIST, 0, 0, 0, 0, NULL }
+};
+
+/**
+ * Subagent initialization
+ */
+static BOOL SubagentInit(Config *config)
+{
+   ssh_init();
+   nxlog_debug(2, _T("SSH: using libssh version %hs"), ssh_version(0));
+       return TRUE;
+}
+
+/**
+ * Called by master agent at unload
+ */
+static void SubagentShutdown()
+{
+}
+
+/**
+ * Supported parameters
+ */
+static NETXMS_SUBAGENT_PARAM m_parameters[] =
+{
+       { _T("SSH.Command(*)"), H_SSHCommand, NULL, DCI_DT_STRING, _T("Result of command execution") },
+};
+
+/**
+ * Supported lists
+ */
+static NETXMS_SUBAGENT_LIST m_lists[] =
+{
+   { _T("SSH.Command(*)"), H_SSHCommandList, NULL, _T("Result of command execution") },
+};
+
+/**
+ * Subagent information
+ */
+static NETXMS_SUBAGENT_INFO m_info =
+{
+       NETXMS_SUBAGENT_INFO_MAGIC,
+       _T("PING"), NETXMS_VERSION_STRING,
+       SubagentInit, SubagentShutdown, NULL,
+       sizeof(m_parameters) / sizeof(NETXMS_SUBAGENT_PARAM),
+       m_parameters,
+   sizeof(m_lists) / sizeof(NETXMS_SUBAGENT_LIST),
+   m_lists,
+       0, NULL,        // tables
+   0, NULL,    // actions
+       0, NULL // push parameters
+};
+
+/**
+ * Entry point for NetXMS agent
+ */
+DECLARE_SUBAGENT_ENTRY_POINT(SSH)
+{
+       *ppInfo = &m_info;
+       return TRUE;
+}
+
+#ifdef _WIN32
+
+/**
+ * DLL entry point
+ */
+BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
+{
+       if (dwReason == DLL_PROCESS_ATTACH)
+               DisableThreadLibraryCalls(hInstance);
+       return TRUE;
+}
+
+#endif
diff --git a/src/agent/subagents/ssh/session.cpp b/src/agent/subagents/ssh/session.cpp
new file mode 100644 (file)
index 0000000..6509eff
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+** NetXMS SSH subagent
+** Copyright (C) 2004-2016 Victor Kirhenshtein
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+** File: session.cpp
+**
+**/
+
+#include "ssh_subagent.h"
+
+/**
+ * SSH session constructor
+ */
+SSHSession::SSHSession(const InetAddress& addr, UINT16 port)
+{
+   m_addr = addr;
+   m_port = port;
+   m_session = NULL;
+}
+
+/**
+ * SSH session destructor
+ */
+SSHSession::~SSHSession()
+{
+   disconnect();
+}
+
+/**
+ * Connect to server
+ */
+bool SSHSession::connect(const TCHAR *user, const TCHAR *password)
+{
+   if (m_session != NULL)
+      return false;  // already connected
+
+   m_session = ssh_new();
+   if (m_session == NULL)
+      return false;  // cannot create session
+
+   bool success = false;
+
+   char hostname[64];
+   ssh_options_set(m_session, SSH_OPTIONS_HOST, m_addr.toStringA(hostname));
+   ssh_options_set(m_session, SSH_OPTIONS_PORT, &m_port);
+   long timeout = 2;
+   ssh_options_set(m_session, SSH_OPTIONS_TIMEOUT, &timeout);
+#ifdef UNICODE
+   char mbuser[256];
+   WideCharToMultiByte(CP_UTF8, 0, user, -1, mbuser, 256, NULL, NULL);
+   ssh_options_set(m_session, SSH_OPTIONS_USER, mbuser);
+#else
+   ssh_options_set(m_session, SSH_OPTIONS_USER, user);
+#endif
+
+   if (ssh_connect(m_session) == SSH_OK)
+   {
+#ifdef UNICODE
+      char mbpassword[256];
+      WideCharToMultiByte(CP_UTF8, 0, password, -1, mbpassword, 256, NULL, NULL);
+      if (ssh_userauth_password(m_session, NULL, mbpassword) == SSH_AUTH_SUCCESS)
+#else
+      if (ssh_userauth_password(m_session, NULL, password) == SSH_AUTH_SUCCESS)
+#endif
+      {
+         success = true;
+      }
+      else
+      {
+         nxlog_debug(6, _T("SSH: login as %s on %s:%d failed"), user, (const TCHAR *)m_addr.toString(), m_port);
+      }
+   }
+   else
+   {
+      nxlog_debug(6, _T("SSH: connect to %s:%d failed"), (const TCHAR *)m_addr.toString(), m_port);
+   }
+
+   if (!success)
+   {
+      if (ssh_is_connected(m_session))
+         ssh_disconnect(m_session);
+      ssh_free(m_session);
+      m_session = NULL;
+   }
+   return success;
+}
+
+/**
+ * Disconnect from server
+ */
+void SSHSession::disconnect()
+{
+   if (m_session == NULL)
+      return;
+
+   if (ssh_is_connected(m_session))
+      ssh_disconnect(m_session);
+   ssh_free(m_session);
+   m_session = NULL;
+}
+
+/**
+ * Execute command and capture output
+ */
+StringList *SSHSession::execute(const TCHAR *command)
+{
+   if ((m_session == NULL) || !ssh_is_connected(m_session))
+      return NULL;
+
+   ssh_channel channel = ssh_channel_new(m_session);
+   if (channel == NULL)
+      return NULL;
+
+   StringList *output = NULL;
+   if (ssh_channel_open_session(channel) == SSH_OK)
+   {
+#ifdef UNICODE
+      char *mbcmd = UTF8StringFromWideString(command);
+      if (ssh_channel_request_exec(channel, mbcmd) == SSH_OK)
+#else
+      if (ssh_channel_request_exec(channel, command) == SSH_OK)
+#endif
+      {
+         output = new StringList();
+         char buffer[8192];
+         int nbytes = ssh_channel_read(channel, buffer, sizeof(buffer) - 1, 0);
+         int offset = 0;
+         while(nbytes > 0)
+         {
+            buffer[nbytes + offset] = 0;
+            char *curr = buffer;
+            char *eol = strchr(curr, '\n');
+            while(eol != NULL)
+            {
+               *eol = 0;
+               output->addMBString(curr);
+               curr = eol + 1;
+               eol = strchr(curr, '\n');
+            }
+            offset = strlen(curr);
+            if (offset > 0)
+               memmove(buffer, curr, offset);
+            nbytes = ssh_channel_read(channel, &buffer[offset], sizeof(buffer) - offset - 1, 0);
+         }
+         if (nbytes == 0)
+         {
+            if (offset > 0)
+            {
+               buffer[offset] = 0;
+               output->addMBString(buffer);
+            }
+            ssh_channel_send_eof(channel);
+         }
+         else
+         {
+            delete_and_null(output);
+         }
+      }
+      else
+      {
+         nxlog_debug(6, _T("SSH: command \"%s\" execution on %s:%d failed"), command, (const TCHAR *)m_addr.toString(), m_port);
+      }
+      ssh_channel_close(channel);
+   }
+   else
+   {
+      nxlog_debug(6, _T("SSH: cannot open channel on %s:%d"), (const TCHAR *)m_addr.toString(), m_port);
+   }
+   ssh_channel_free(channel);
+   return output;
+}
diff --git a/src/agent/subagents/ssh/ssh_subagent.h b/src/agent/subagents/ssh/ssh_subagent.h
new file mode 100644 (file)
index 0000000..1437b00
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+** NetXMS SSH subagent
+** Copyright (C) 2004-2016 Victor Kirhenshtein
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU General Public License for more details.
+**
+** You should have received a copy of the GNU General Public License
+** along with this program; if not, write to the Free Software
+** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+**
+** File: ssh.h
+**
+**/
+
+#ifndef _ssh_subagent_h
+#define _ssh_subagent_h_
+
+#include <nms_common.h>
+#include <nms_util.h>
+#include <nms_agent.h>
+#include <libssh/libssh.h>
+
+/**
+ * SSH session
+ */
+class SSHSession
+{
+private:
+   InetAddress m_addr;
+   unsigned int m_port;
+   ssh_session m_session;
+
+public:
+   SSHSession(const InetAddress& addr, UINT16 port);
+   ~SSHSession();
+
+   bool connect(const TCHAR *user, const TCHAR *password);
+   void disconnect();
+   StringList *execute(const TCHAR *command);
+};
+
+/* handlers */
+LONG H_SSHCommand(const TCHAR *param, const TCHAR *arg, TCHAR *value, AbstractCommSession *session);
+LONG H_SSHCommandList(const TCHAR *param, const TCHAR *arg, StringList *value, AbstractCommSession *session);
+
+#endif