Added functionality to set max size of downloaded file from agent and option to tail...
authorzev <zev@radensolutions.com>
Fri, 3 Jan 2014 14:35:31 +0000 (16:35 +0200)
committerzev <zev@radensolutions.com>
Fri, 3 Jan 2014 14:36:06 +0000 (16:36 +0200)
37 files changed:
.gitignore
include/nms_agent.h
include/nms_common.h
include/nms_cscp.h
include/nms_util.h
include/nxcpapi.h
include/nxlog.h
src/agent/core/Makefile.am
src/agent/core/logmonitoring.cpp [new file with mode: 0644]
src/agent/core/nxagentd.cpp
src/agent/core/nxagentd.h
src/agent/core/session.cpp
src/install/files
src/java/netxms-base/src/main/java/org/netxms/base/NXCPCodes.java
src/java/netxms-client/src/main/java/org/netxms/client/NXCSession.java
src/java/netxms-client/src/test/java/org/netxms/client/ServerFilesTest.java
src/java/netxms-client/src/test/java/org/netxms/client/TestConstants.java [new file with mode: 0644]
src/java/netxms-eclipse/ObjectTools/src/org/netxms/ui/eclipse/objecttools/ObjectToolsDynamicMenu.java
src/java/netxms-eclipse/ObjectTools/src/org/netxms/ui/eclipse/objecttools/propertypages/General.java
src/java/netxms-eclipse/ObjectTools/src/org/netxms/ui/eclipse/objecttools/views/FileViewer.java
src/libnetxms/agent.cpp
src/libnetxms/nxcp.cpp
src/libnxcl/libnxcl.h
src/libnxcl/package.cpp
src/libnxcl/session.cpp
src/server/core/Makefile.am
src/server/core/agent.cpp
src/server/core/download_job.cpp
src/server/core/filemonitoring.cpp [new file with mode: 0644]
src/server/core/session.cpp
src/server/include/nms_core.h
src/server/include/nxcore_jobs.h
src/server/include/nxsrvapi.h
src/server/libnxsrv/agent.cpp
webui/webapp/ObjectTools/src/org/netxms/ui/eclipse/objecttools/ObjectToolsDynamicMenu.java
webui/webapp/ObjectTools/src/org/netxms/ui/eclipse/objecttools/propertypages/General.java
webui/webapp/ObjectTools/src/org/netxms/ui/eclipse/objecttools/views/FileViewer.java

index 36629ee..905b9a4 100644 (file)
@@ -1,9 +1,11 @@
 *.aps
 *.bak
+*.cbp
 *.iml
 *.ipr
 *.iws
 *.la
+*.layout
 *.lo
 *.ncb
 *.nlm
index 7ac3436..2bf881e 100644 (file)
@@ -517,7 +517,7 @@ void LIBNETXMS_EXPORTABLE AgentWriteDebugLog(int level, const TCHAR *format, ...
 void LIBNETXMS_EXPORTABLE AgentWriteDebugLog2(int level, const TCHAR *format, va_list args);
 void LIBNETXMS_EXPORTABLE AgentSendTrap(UINT32 dwEvent, const TCHAR *eventName, const char *pszFormat, ...);
 void LIBNETXMS_EXPORTABLE AgentSendTrap2(UINT32 dwEvent, const TCHAR *eventName, int nCount, TCHAR **ppszArgList);
-BOOL LIBNETXMS_EXPORTABLE AgentSendFileToServer(void *session, UINT32 requestId, const TCHAR *file, long offset);
+BOOL LIBNETXMS_EXPORTABLE AgentSendFileToServer(void *session, UINT32 requestId, const TCHAR *file, long offset, long sizeLimit);
 BOOL LIBNETXMS_EXPORTABLE AgentPushParameterData(const TCHAR *parameter, const TCHAR *value);
 BOOL LIBNETXMS_EXPORTABLE AgentPushParameterDataInt32(const TCHAR *parameter, LONG value);
 BOOL LIBNETXMS_EXPORTABLE AgentPushParameterDataUInt32(const TCHAR *parameter, UINT32 value);
index 27e56b3..ae53b2c 100644 (file)
 #define MD5_DIGEST_SIZE       16
 #define SHA1_DIGEST_SIZE      20
 
+#define FILE_BUFFER_SIZE      32768
+
 /**
  * Compatibility defines for C sources
  */
index 4a2355b..c1f293f 100644 (file)
@@ -482,6 +482,8 @@ typedef struct
 #define CMD_SHUTDOWN                   0x011A
 #define CMD_SNMP_TRAP                  0x011B
 #define CMD_GET_SUBNET_ADDRESS_MAP     0x011C
+#define CMD_FILE_MONITORING            0x011D
+#define CMD_CANCEL_FILE_MONITORING     0x011E
 
 #define CMD_RS_LIST_REPORTS            0x1100
 #define CMD_RS_GET_REPORT              0x1101
@@ -950,6 +952,9 @@ typedef struct
 #define VID_REQUEST_ID              ((UINT32)455)
 #define VID_ADDRESS_MAP             ((UINT32)456)
 #define VID_XMPP_ID                 ((UINT32)457)
+#define VID_FILE_SIZE_LIMIT         ((UINT32)458)
+#define VID_FILE_FOLLOW             ((UINT32)459)
+#define VID_FILE_DATA               ((UINT32)460)
 
 // Base variabe for single threshold in message
 #define VID_THRESHOLD_BASE          ((UINT32)0x00800000)
index ed0d037..01157b8 100644 (file)
@@ -1283,7 +1283,7 @@ void LIBNETXMS_EXPORTABLE StartMainLoop(ThreadFunction pfSignalHandler, ThreadFu
 void LIBNETXMS_EXPORTABLE InitSubAgentAPI(void (* writeLog)(int, int, const TCHAR *),
                                           void (* sendTrap1)(UINT32, const TCHAR *, const char *, va_list),
                                           void (* sendTrap2)(UINT32, const TCHAR *, int, TCHAR **),
-                                          bool (* sendFile)(void *, UINT32, const TCHAR *, long),
+                                          bool (* sendFile)(void *, UINT32, const TCHAR *, long, long),
                                           bool (* pushData)(const TCHAR *, const TCHAR *, UINT32));
 
 #endif
index af57a8c..90b61d8 100644 (file)
@@ -1,4 +1,4 @@
-/* 
+/*
 ** NetXMS - Network Management System
 ** NXCP API
 ** Copyright (C) 2003-2010 Victor Kirhenshtein
@@ -230,7 +230,7 @@ CSCP_MESSAGE LIBNETXMS_EXPORTABLE *CreateRawNXCPMessage(WORD wCode, UINT32 dwId,
                                                         CSCP_MESSAGE *pBuffer);
 TCHAR LIBNETXMS_EXPORTABLE *NXCPMessageCodeName(WORD wCode, TCHAR *pszBuffer);
 BOOL LIBNETXMS_EXPORTABLE SendFileOverNXCP(SOCKET hSocket, UINT32 dwId, const TCHAR *pszFile,
-                                           NXCPEncryptionContext *pCtx, long offset,
+                                           NXCPEncryptionContext *pCtx, long offset, long sizeLimit,
                                                                                                                 void (* progressCallback)(INT64, void *), void *cbArg,
                                                                                                                 MUTEX mutex);
 BOOL LIBNETXMS_EXPORTABLE NXCPGetPeerProtocolVersion(SOCKET hSocket, int *pnVersion, MUTEX mutex);
index 97d839d..ba11c15 100644 (file)
@@ -64,4 +64,5 @@ typedef struct
    char szMessage[MAX_LOG_MSG_LENGTH];
 } NX_SYSLOG_RECORD;
 
+
 #endif
index af45968..acdf458 100644 (file)
@@ -1,8 +1,8 @@
 AM_CPPFLAGS=-I@top_srcdir@/include
 bin_PROGRAMS = nxagentd
 nxagentd_SOURCES = messages.c actions.cpp appagent.cpp comm.cpp config.cpp \
-                   ctrl.cpp epp.cpp exec.cpp extagent.cpp getparam.cpp master.cpp \
-                   nxagentd.cpp policy.cpp push.cpp register.cpp sd.cpp \
+                   ctrl.cpp epp.cpp exec.cpp extagent.cpp getparam.cpp logmonitoring.cpp\
+                   master.cpp nxagentd.cpp policy.cpp push.cpp register.cpp sd.cpp \
                    session.cpp snmpproxy.cpp static_subagents.cpp subagent.cpp \
                    sysinfo.cpp tools.cpp trap.cpp upgrade.cpp watchdog.cpp
 if USE_INTERNAL_EXPAT
diff --git a/src/agent/core/logmonitoring.cpp b/src/agent/core/logmonitoring.cpp
new file mode 100644 (file)
index 0000000..b3faf2d
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+** NetXMS multiplatform core agent
+** Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 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: logmonitoring.cpp
+**
+**/
+
+#include "nxagentd.h"
+
+/**
+ * Max NXCP message size
+ */
+#define MAX_MSG_SIZE    262144
+
+MonitoredFileList::MonitoredFileList()
+{
+   m_mutex = MutexCreate();
+   m_monitoredFiles.setOwner(true);
+};
+
+MonitoredFileList::~MonitoredFileList()
+{
+   MutexDestroy(m_mutex);
+};
+
+void MonitoredFileList::addMonitoringFile(const TCHAR *fileName)
+{
+   Lock();
+   bool alreadyMonitored = false;
+   for(int i = 0; i < m_monitoredFiles.size(); i++)
+   {
+      m_newFile = m_monitoredFiles.get(i);
+      if (_tcscmp(m_newFile->fileName, fileName) == 0)
+      {
+         alreadyMonitored = true;
+         m_newFile->monitoringCount++;
+      }
+   }
+
+   if(!alreadyMonitored)
+   {
+      m_newFile = new MONITORED_FILE();
+      _tcscpy(m_newFile->fileName, fileName);
+      m_newFile->monitoringCount = 1;
+      m_monitoredFiles.add(m_newFile);
+   }
+   Unlock();
+};
+
+bool MonitoredFileList::checkFileMonitored(const TCHAR *fileName)
+{
+   Lock();
+   bool result = false;
+   for(int i = 0; i < m_monitoredFiles.size() ; i++)
+   {
+      m_newFile = m_monitoredFiles.get(i);
+      if (_tcscmp(m_newFile->fileName, fileName) == 0)
+      {
+         result = true;
+      }
+   }
+   Unlock();
+   return result;
+};
+
+bool MonitoredFileList::removeMonitoringFile(const TCHAR *fileName)
+{
+   Lock();
+   bool alreadyMonitored = false;
+   for(int i = 0; i < m_monitoredFiles.size() ; i++)
+   {
+      m_newFile = m_monitoredFiles.get(i);
+      if (_tcscmp(m_newFile->fileName, fileName) == 0)
+      {
+         alreadyMonitored = true;
+         m_newFile->monitoringCount--;
+         if(0 == m_newFile->monitoringCount)
+         {
+            m_monitoredFiles.remove(i);
+         }
+      }
+   }
+
+   if(!alreadyMonitored)
+   {
+      DebugPrintf(INVALID_INDEX, 6, _T("MonitoredFileList::removeMonitoringFile: Tryed to delete not existing file %s."), fileName);
+   }
+   Unlock();
+   return alreadyMonitored;
+};
+
+void MonitoredFileList::Lock()
+{
+   MutexLock(m_mutex);
+};
+
+void MonitoredFileList::Unlock()
+{
+   MutexUnlock(m_mutex);
+};
+
+THREAD_RESULT THREAD_CALL SendFileUpdatesOverNXCP(void* args)
+{
+   FolowData* flData = (reinterpret_cast<FolowData*>(args));
+   int hFile, threadSleepTime = 1;
+   BYTE* readBytes = NULL;
+   BOOL bResult = FALSE;
+   CSCPMessage *pMsg;
+   UINT32 readSize, rcc;
+
+   bool follow = true;
+   hFile = _topen(flData->pszFile, O_RDONLY | O_BINARY);
+   #ifdef _WIN32
+            struct _stat fs;
+   #else
+            struct stat st;
+   #endif
+   fstat(hFile, &st);
+   flData->offset = st.st_size;
+   ThreadSleep(threadSleepTime);
+   int headerSize = CSCP_HEADER_SIZE + MAX_PATH*2;
+
+   if (hFile == -1)
+   {
+      DebugPrintf(INVALID_INDEX, 6, _T("SendFileUpdatesOverNXCP: File does not exists or couldn't be opened. File: %s."), flData->pszFile);
+      g_monitorFileList.removeMonitoringFile(flData->pszFile);
+      return THREAD_OK;
+   }
+   while (follow)
+   {
+      fstat(hFile, &st);
+      long newOffset = st.st_size;
+      if(flData->offset < newOffset)
+      {
+         readSize = newOffset - flData->offset;
+         for(int i = readSize; i > 0; i = i - readSize)
+         {
+            if(readSize+1+headerSize > MAX_MSG_SIZE)
+            {
+               readSize = MAX_MSG_SIZE - headerSize;
+               newOffset = flData->offset + readSize;
+            }
+            pMsg = new CSCPMessage();
+            pMsg->SetCode(CMD_FILE_MONITORING);
+            pMsg->SetId(0);
+            pMsg->SetVariable(VID_FILE_NAME, flData->pszFile, MAX_PATH);
+
+            lseek(hFile, flData->offset, SEEK_SET);
+            readBytes = (BYTE*)malloc(readSize);
+            readSize = read(hFile, readBytes, readSize);
+            DebugPrintf(INVALID_INDEX, 6, _T("SendFileUpdatesOverNXCP: %d bytes will be sent."), readSize);
+#ifdef UNICODE
+            TCHAR* text;
+            text = WideStringFromMBString((char*)readBytes);
+            pMsg->SetVariable(VID_FILE_DATA, text, readSize);
+#else
+            pMsg->SetVariable(VID_FILE_DATA, (TCHAR*)readBytes, readSize);
+#endif
+
+
+            flData->offset = newOffset;
+
+            MutexLock(g_hSessionListAccess);
+            for(int i = 0; i < g_dwMaxSessions; i++)
+            {
+               if (g_pSessionList[i] != NULL)
+               {
+                  g_pSessionList[i]->sendMessage(pMsg);
+                  break;
+               }
+            }
+            MutexUnlock(g_hSessionListAccess);
+
+            safe_free(readBytes);
+            delete pMsg;
+         }
+      }
+
+      ThreadSleep(threadSleepTime);
+      if(!g_monitorFileList.checkFileMonitored(flData->pszFile))
+      {
+         follow = false;
+      }
+   }
+   delete flData->pszFile;
+   delete flData;
+   close(hFile);
+   return THREAD_OK;
+};
index e4a22d4..ad47509 100644 (file)
@@ -125,6 +125,7 @@ UINT32 g_dwStartupDelay = 0;
 UINT32 g_dwMaxSessions = 32;
 UINT32 g_debugLevel = (UINT32)NXCONFIG_UNINITIALIZED_VALUE;
 Config *g_config;
+MonitoredFileList g_monitorFileList;
 #ifdef _WIN32
 UINT32 g_dwIdleTimeout = 60;   // Session idle timeout
 #else
@@ -541,12 +542,11 @@ static void LoadPlatformSubagent()
 /**
  * Send file to server (subagent API)
  */
-static bool SendFileToServer(void *session, UINT32 requestId, const TCHAR *file, long offset)
+static bool SendFileToServer(void *session, UINT32 requestId, const TCHAR *file, long offset, long sizeLimit)
 {
        if (session == NULL)
                return false;
-
-       return ((CommSession *)session)->sendFile(requestId, file, offset);
+       return ((CommSession *)session)->sendFile(requestId, file, offset, sizeLimit);
 }
 
 /**
index 4821ffd..cb77b1a 100644 (file)
@@ -328,6 +328,7 @@ private:
    void action(CSCPMessage *pRequest, CSCPMessage *pMsg);
    void recvFile(CSCPMessage *pRequest, CSCPMessage *pMsg);
    void getLocalFile(CSCPMessage *pRequest, CSCPMessage *pMsg);
+   void cancelFileMonitoring(CSCPMessage *pRequest, CSCPMessage *pMsg);
    void getFileDetails(CSCPMessage *pRequest, CSCPMessage *pMsg);
    UINT32 upgrade(CSCPMessage *pRequest);
    UINT32 setupProxyConnection(CSCPMessage *pRequest);
@@ -351,7 +352,7 @@ public:
 
    void sendMessage(CSCPMessage *pMsg) { m_pSendQueue->Put(pMsg->CreateMessage()); }
    void sendRawMessage(CSCP_MESSAGE *pMsg) { m_pSendQueue->Put(nx_memdup(pMsg, ntohl(pMsg->dwSize))); }
-       bool sendFile(UINT32 requestId, const TCHAR *file, long offset);
+       bool sendFile(UINT32 requestId, const TCHAR *file, long offset, long sizeLimit);
 
        UINT32 getServerAddress() { return m_dwHostAddr; }
 
@@ -451,6 +452,43 @@ void StartStorageDiscoveryConnector();
 void StartControlConnector();
 bool SendControlMessage(CSCPMessage *msg);
 
+/**
+ * File monitoring
+ */
+struct MONITORED_FILE
+{
+   TCHAR fileName[MAX_PATH];
+   int monitoringCount;
+};
+
+struct FolowData
+{
+   const TCHAR *pszFile;
+   long offset;
+       void *cbArg;
+};
+
+class MonitoredFileList
+{
+private:
+   MUTEX m_mutex;
+   ObjectArray<MONITORED_FILE>  m_monitoredFiles;
+   MONITORED_FILE* m_newFile;
+
+public:
+   MonitoredFileList();
+   ~MonitoredFileList();
+   void addMonitoringFile(const TCHAR *fileName);
+   bool checkFileMonitored(const TCHAR *fileName);
+   bool removeMonitoringFile(const TCHAR *fileName);
+
+private:
+   void Lock();
+   void Unlock();
+};
+
+THREAD_RESULT THREAD_CALL SendFileUpdatesOverNXCP(void *arg);
+
 #ifdef _WIN32
 
 void InitService();
@@ -501,6 +539,7 @@ extern UINT32 g_dwRejectedConnections;
 
 extern CommSession **g_pSessionList;
 extern MUTEX g_hSessionListAccess;
+extern MonitoredFileList g_monitorFileList;
 
 #ifdef _WIN32
 extern TCHAR g_windowsEventSourceName[];
index 058b23e..494d67b 100644 (file)
@@ -1,4 +1,4 @@
-/* 
+/*
 ** NetXMS multiplatform core agent
 ** Copyright (C) 2003-2013 Victor Kirhenshtein
 **
@@ -225,7 +225,7 @@ void CommSession::readThread()
 
                         close(m_hCurrFile);
                         m_hCurrFile = -1;
-                  
+
                         msg.SetCode(CMD_REQUEST_COMPLETED);
                         msg.SetId(pRawMsg->dwId);
                         msg.SetVariable(VID_RCC, ERR_SUCCESS);
@@ -239,7 +239,7 @@ void CommSession::readThread()
 
                      close(m_hCurrFile);
                      m_hCurrFile = -1;
-               
+
                      msg.SetCode(CMD_REQUEST_COMPLETED);
                      msg.SetId(pRawMsg->dwId);
                      msg.SetVariable(VID_RCC, ERR_IO_FAILURE);
@@ -432,6 +432,9 @@ void CommSession::processingThread()
             case CMD_GET_AGENT_FILE:
                getLocalFile(pMsg, &msg);
                break;
+            case CMD_CANCEL_FILE_MONITORING:
+               cancelFileMonitoring(pMsg, &msg);
+               break;
             case CMD_GET_FILE_DETAILS:
                getFileDetails(pMsg, &msg);
                break;
@@ -708,7 +711,17 @@ void CommSession::getLocalFile(CSCPMessage *pRequest, CSCPMessage *pMsg)
        {
                TCHAR fileName[MAX_PATH];
                pRequest->GetVariableStr(VID_FILE_NAME, fileName, MAX_PATH);
-               sendFile(pRequest->GetId(), fileName, pRequest->GetVariableLong(VID_FILE_OFFSET));
+               bool result = sendFile(pRequest->GetId(), fileName, pRequest->GetVariableLong(VID_FILE_OFFSET), pRequest->GetVariableLong(VID_FILE_SIZE_LIMIT));
+               if(pRequest->GetVariableShort(VID_FILE_FOLLOW) != 0 && result)
+      {
+         TCHAR* fName = _tcsdup(fileName);
+         g_monitorFileList.addMonitoringFile(fName);
+         FolowData *flData = new FolowData();
+         flData->cbArg = this;
+         flData->pszFile = fName;
+         flData->offset = 0;
+         ThreadCreateEx(SendFileUpdatesOverNXCP, 0, (void*)flData);
+      }
                pMsg->SetVariable(VID_RCC, ERR_SUCCESS);
        }
        else
@@ -717,6 +730,29 @@ void CommSession::getLocalFile(CSCPMessage *pRequest, CSCPMessage *pMsg)
        }
 }
 
+/**
+ * Cancel file monitoring
+ */
+void CommSession::cancelFileMonitoring(CSCPMessage *pRequest, CSCPMessage *pMsg)
+{
+       if (m_bMasterServer)
+       {
+               TCHAR fileName[MAX_PATH];
+               pRequest->GetVariableStr(VID_FILE_NAME, fileName, MAX_PATH);
+      if(g_monitorFileList.removeMonitoringFile(fileName))
+      {
+         pMsg->SetVariable(VID_RCC, ERR_SUCCESS);
+      }
+      else
+      {
+         pMsg->SetVariable(VID_RCC, ERR_BAD_ARGUMENTS);
+      }
+       }
+       else
+       {
+               pMsg->SetVariable(VID_RCC, ERR_ACCESS_DENIED);
+       }
+}
 
 //
 // Get local file details
@@ -813,9 +849,9 @@ static void SendFileProgressCallback(INT64 bytesTransferred, void *cbArg)
        ((CommSession *)cbArg)->updateTimeStamp();
 }
 
-bool CommSession::sendFile(UINT32 requestId, const TCHAR *file, long offset)
+bool CommSession::sendFile(UINT32 requestId, const TCHAR *file, long offset, long sizeLimit)
 {
-       return SendFileOverNXCP(m_hSocket, requestId, file, m_pCtx, offset, SendFileProgressCallback, this, m_socketWriteMutex) ? true : false;
+       return SendFileOverNXCP(m_hSocket, requestId, file, m_pCtx, offset, sizeLimit, SendFileProgressCallback, this, m_socketWriteMutex) ? true : false;
 }
 
 
@@ -855,7 +891,7 @@ void CommSession::getConfig(CSCPMessage *pMsg)
 {
    if (m_bMasterServer)
    {
-      pMsg->SetVariable(VID_RCC, 
+      pMsg->SetVariable(VID_RCC,
          pMsg->SetVariableFromFile(VID_CONFIG_FILE, g_szConfigFile) ? ERR_SUCCESS : ERR_IO_FAILURE);
    }
    else
index 44d9be5..58d9376 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 44d9be5989b85cf5c24d1467f44b8ad2fdfb4299
+Subproject commit 58d9376f9733cfaed688f7bf85e4946eada850af
index a433850..10affbd 100644 (file)
@@ -308,6 +308,8 @@ public class NXCPCodes
        public static final int CMD_SHUTDOWN = 0x011A;
        public static final int CMD_SNMP_TRAP = 0x011B;
        public static final int CMD_GET_SUBNET_ADDRESS_MAP = 0x011C;
+       public static final int CMD_FILE_MONITORING = 0x011D;
+       public static final int CMD_CANCEL_FILE_MONITORING = 0x011E;
 
        // CMD_RS_ - Reporting Server related codes
        public static final int CMD_RS_LIST_REPORTS = 0x1100;
@@ -775,6 +777,9 @@ public class NXCPCodes
    public static final long VID_REQUEST_ID = 455;
    public static final long VID_ADDRESS_MAP = 456;
    public static final long VID_XMPP_ID = 457;
+   public static final long VID_FILE_SIZE_LIMIT = 458;
+   public static final long VID_FILE_FOLLOW = 459;
+   public static final long VID_FILE_DATA = 460;
 
        public static final long VID_ACL_USER_BASE = 0x00001000L;
        public static final long VID_ACL_USER_LAST = 0x00001FFFL;
index a6f755c..50a0380 100644 (file)
@@ -167,6 +167,9 @@ public class NXCSession implements Session, ScriptLibraryManager, UserManager, S
 
    // Received files
    private Map<Long, NXCReceivedFile> receivedFiles = new HashMap<Long, NXCReceivedFile>();
+   
+   // Received file updates(for file monitoring)
+   private Map<String, String> recievdUpdates = new HashMap<String, String>();
 
    // Server information
    private String serverVersion = "(unknown)";
@@ -552,6 +555,9 @@ public class NXCSession implements Session, ScriptLibraryManager, UserManager, S
                   case NXCPCodes.CMD_FILE_DATA:
                      processFileData(msg);
                      break;
+                  case NXCPCodes.CMD_FILE_MONITORING:
+                     processFileTail(msg);
+                     break;
                   case NXCPCodes.CMD_ABORT_FILE_TRANSFER:
                      processFileTransferError(msg);
                      break;
@@ -689,6 +695,23 @@ public class NXCSession implements Session, ScriptLibraryManager, UserManager, S
          long data = msg.getVariableAsInt64(NXCPCodes.VID_NOTIFICATION_DATA);
          sendNotification(new NXCNotification(code, data));
       }
+      
+      /**
+       * Process CMD_FILE_MONITORING message
+       *
+       * @param msg NXCP message
+       */
+      private void processFileTail(final NXCPMessage msg)
+      {
+         String fileName = msg.getVariableAsString(NXCPCodes.VID_FILE_NAME);
+         String fileContent = msg.getVariableAsString(NXCPCodes.VID_FILE_DATA);
+         
+         synchronized(recievdUpdates)
+         {
+            recievdUpdates.put(fileName, fileContent);
+            recievdUpdates.notifyAll();
+         }
+      }
 
       /**
        * Process file data
@@ -1281,6 +1304,43 @@ public class NXCSession implements Session, ScriptLibraryManager, UserManager, S
       }
       return file;
    }
+   
+   /**
+    * Wait for specific file tail to arrive
+    *
+    * @param fileName      Waiting file name
+    * @param timeout       Wait timeout in milliseconds
+    * @return Received tail string or null in case of failure
+    */
+   public String waitForFileTail(String fileName, final int timeout)
+   {
+      int timeRemaining = timeout;
+      String tail = null;
+
+      while(timeRemaining > 0)
+      {
+         synchronized(recievdUpdates)
+         {
+            tail = recievdUpdates.get(fileName);
+            if (tail != null)
+            {
+               recievdUpdates.remove(fileName);
+               break;
+            }
+
+            long startTime = System.currentTimeMillis();
+            try
+            {
+               recievdUpdates.wait(timeRemaining);
+            }
+            catch(InterruptedException e)
+            {
+            }
+            timeRemaining -= System.currentTimeMillis() - startTime;
+         }
+      }
+      return tail;
+   }
 
    /**
     * Execute simple commands (without arguments and only returning RCC)
@@ -5769,16 +5829,35 @@ public class NXCSession implements Session, ScriptLibraryManager, UserManager, S
     * @throws IOException  if socket or file I/O error occurs
     * @throws NXCException if NetXMS server returns an error or operation was timed out
     */
-   public File downloadFileFromAgent(long nodeId, String remoteFileName) throws IOException, NXCException
+   public File downloadFileFromAgent(long nodeId, String remoteFileName, long maxFileSize, boolean follow) throws IOException, NXCException
    {
       final NXCPMessage msg = newMessage(NXCPCodes.CMD_GET_AGENT_FILE);
       msg.setVariableInt32(NXCPCodes.VID_OBJECT_ID, (int) nodeId);
       msg.setVariable(NXCPCodes.VID_FILE_NAME, remoteFileName);
+      msg.setVariableInt32(NXCPCodes.VID_FILE_SIZE_LIMIT , (int)maxFileSize);
+      msg.setVariableInt16(NXCPCodes.VID_FILE_FOLLOW, follow ? 1 : 0);
       sendMessage(msg);
       waitForRCC(msg.getMessageId()); // first confirmation - server job started
       waitForRCC(msg.getMessageId()); // second confirmation - file downloaded to server
       return waitForFile(msg.getMessageId(), 3600000);
    }
+   
+   /**
+    * Cancel file monitoring
+    *
+    * @param nodeId         node object ID
+    * @param remoteFileName fully qualified file name on remote system
+    * @throws IOException  if socket or file I/O error occurs
+    * @throws NXCException if NetXMS server returns an error or operation was timed out
+    */
+   public void cancelFileMonitoring(long nodeId, String remoteFileName) throws IOException, NXCException
+   {
+      final NXCPMessage msg = newMessage(NXCPCodes.CMD_CANCEL_FILE_MONITORING);
+      msg.setVariableInt32(NXCPCodes.VID_OBJECT_ID, (int) nodeId);
+      msg.setVariable(NXCPCodes.VID_FILE_NAME, remoteFileName);
+      sendMessage(msg);
+      waitForRCC(msg.getMessageId());
+   }
 
    /**
     * Delete file from server's file store
index 0e43640..0023979 100644 (file)
@@ -19,6 +19,8 @@
 package org.netxms.client;
 
 import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
 
 /**
  * Test functionality related to server file store
@@ -41,9 +43,31 @@ public class ServerFilesTest extends SessionTest
        {
                final NXCSession session = connect();
                
-               File file = session.downloadFileFromAgent(11, "D:\\TEMP\\SyslogGen.pdf");
+               File file = session.downloadFileFromAgent(TestConstants.NodeID, TestConstants.FileName, TestConstants.FileOfset, true);
+               //check that server returned file with correct size (offset should be less than size of file)
+               assertEquals(file.length(), TestConstants.FileOfset); 
+               String content = null;
+          try {
+              FileReader reader = new FileReader(file);
+              char[] chars = new char[(int) file.length()];
+              reader.read(chars);
+              content = new String(chars);
+              reader.close();
+          } catch (IOException e) {
+              e.printStackTrace();
+          }
+               System.out.println("Downloaded content is: \n" + content);
                System.out.println("*** Downloaded file: " + file.getAbsolutePath());
                
+               //get tails of provided file
+               int i = 3;
+               while(i > 0)
+               {
+                  System.out.println("Tail content: \n" + session.waitForFileTail(TestConstants.FileName, 30000));
+                  i--;
+               }
+               session.cancelFileMonitoring(TestConstants.NodeID, TestConstants.FileName);
+               System.out.println("Monitoring have been canceled.\n");
                session.disconnect();
        }
 }
diff --git a/src/java/netxms-client/src/test/java/org/netxms/client/TestConstants.java b/src/java/netxms-client/src/test/java/org/netxms/client/TestConstants.java
new file mode 100644 (file)
index 0000000..2495bcb
--- /dev/null
@@ -0,0 +1,9 @@
+package org.netxms.client;
+
+public class TestConstants
+{
+   //Constants for file download
+   public static int NodeID = 134;
+   public static String FileName = "/var/log/netxmsd";
+   public static int FileOfset = 3000;
+}
index 920ded3..b9f0849 100644 (file)
@@ -448,7 +448,11 @@ public class ObjectToolsDynamicMenu extends ContributionItem implements IWorkben
        private void executeFileDownload(final AbstractNode node, final ObjectTool tool)
        {
                final NXCSession session = (NXCSession)ConsoleSharedData.getSession();
-               final String fileName = substituteMacros(tool.getData(), node);
+               String[] parameters = tool.getData().split("\u007F");
+               
+               final String fileName = substituteMacros(parameters[0], node);
+               final int maxFileSize = Integer.parseInt(parameters[1]);
+               final boolean follow = parameters[2].equals("true") ? true : false;
                
                ConsoleJob job = new ConsoleJob(Messages.get().ObjectToolsDynamicMenu_DownloadFromAgent, null, Activator.PLUGIN_ID, null) {
                        @Override
@@ -460,7 +464,7 @@ public class ObjectToolsDynamicMenu extends ContributionItem implements IWorkben
                        @Override
                        protected void runInternal(IProgressMonitor monitor) throws Exception
                        {
-                               final File file = session.downloadFileFromAgent(node.getObjectId(), fileName);
+                               final File file = session.downloadFileFromAgent(node.getObjectId(), fileName, maxFileSize, follow); 
                                runInUIThread(new Runnable() {
                                        @Override
                                        public void run()
@@ -470,7 +474,7 @@ public class ObjectToolsDynamicMenu extends ContributionItem implements IWorkben
                                                {
                                                        String secondaryId = Long.toString(node.getObjectId()) + "&" + URLEncoder.encode(fileName, "UTF-8"); //$NON-NLS-1$ //$NON-NLS-2$
                                                        FileViewer view = (FileViewer)window.getActivePage().showView(FileViewer.ID, secondaryId, IWorkbenchPage.VIEW_ACTIVATE);
-                                                       view.showFile(file);
+                                                       view.showFile(file, follow);
                                                }
                                                catch(Exception e)
                                                {
index 4eca2ab..4f481f5 100644 (file)
@@ -32,6 +32,7 @@ import org.eclipse.ui.dialogs.PropertyPage;
 import org.netxms.client.objecttools.ObjectTool;
 import org.netxms.client.objecttools.ObjectToolDetails;
 import org.netxms.ui.eclipse.objecttools.Messages;
+import org.netxms.ui.eclipse.tools.MessageDialogHelper;
 import org.netxms.ui.eclipse.tools.WidgetHelper;
 import org.netxms.ui.eclipse.widgets.LabeledText;
 
@@ -44,10 +45,12 @@ public class General extends PropertyPage
        private LabeledText textName;
        private LabeledText textDescription;
        private LabeledText textData;
+       private LabeledText maxFileLenght;
        private LabeledText textParameter;
        private LabeledText textRegexp;
        private Button checkOutput;
        private Button checkConfirmation;
+       private Button follow;
        private LabeledText textConfirmation;
        private Button radioIndexOID;
        private Button radioIndexValue;
@@ -119,7 +122,23 @@ public class General extends PropertyPage
                                textData.setLabel(Messages.get().General_URL);
                                break;
                        case ObjectTool.TYPE_FILE_DOWNLOAD:
+                          String[] parameters = objectTool.getData().split("\u007F"); //$NON-NLS-1$
+                          
                                textData.setLabel(Messages.get().General_RemoteFileName);
+                               textData.setText((parameters.length > 0) ? parameters[0] : ""); //$NON-NLS-1$
+                               
+                     maxFileLenght  = new LabeledText(dialogArea, SWT.NONE);
+                     maxFileLenght.setLayoutData(gd);
+                               maxFileLenght.setLabel("Maximum file download size");
+                               maxFileLenght.setText((parameters.length > 1) ? parameters[1] : ""); //$NON-NLS-1$
+                                                       
+                               follow = new Button(dialogArea, SWT.CHECK);
+                               follow.setText("Get file updates");
+                               if(parameters.length > 2) //$NON-NLS-1$
+                               {
+                                  follow.setSelection( parameters[2].equals("true") ? true : false);  //$NON-NLS-1$
+                               }       
+                               
                                break;
                        case ObjectTool.TYPE_TABLE_SNMP:
                                textData.setLabel(Messages.get().General_Title);
@@ -241,7 +260,15 @@ public class General extends PropertyPage
                }
                else
                {
-                       objectTool.setData(textData.getText());
+                  if(objectTool.getType() == ObjectTool.TYPE_FILE_DOWNLOAD)
+               {
+                     String tmp = maxFileLenght.getText() == "" ? "0" : maxFileLenght.getText(); //$NON-NLS-1$ //$NON-NLS-2$
+                     objectTool.setData(textData.getText() + "\u007F" + tmp + "\u007F" + follow.getSelection()); //$NON-NLS-1$ //$NON-NLS-2$
+               }
+                  else
+                  {
+                     objectTool.setData(textData.getText());
+                  }
                }
                
                if (checkConfirmation.getSelection())
@@ -296,6 +323,19 @@ public class General extends PropertyPage
        @Override
        public boolean performOk()
        {
+          if(objectTool.getType() == ObjectTool.TYPE_FILE_DOWNLOAD && maxFileLenght.getText() != "")
+          {
+             try
+             {
+                int n = Integer.parseInt(maxFileLenght.getText());
+             }
+             catch(NumberFormatException e)
+             {
+                MessageDialogHelper.openWarning(getShell(), Messages.get().EditColumnDialog_Warning, "As maximum lenght should be given integre value in bytes.");
+                return false;
+             }
+          }
+          
                applyChanges(false);
                return true;
        }
index 4cdccdc..b54b711 100644 (file)
@@ -25,16 +25,21 @@ import java.io.UnsupportedEncodingException;
 import java.net.URLDecoder;
 import java.util.Arrays;
 import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.swt.SWT;
-import org.eclipse.swt.custom.StyledText;
+import org.eclipse.swt.widgets.Text;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.ui.IViewSite;
 import org.eclipse.ui.PartInitException;
 import org.eclipse.ui.part.ViewPart;
+import org.netxms.client.NXCException;
 import org.netxms.client.NXCSession;
 import org.netxms.client.objects.AbstractObject;
+import org.netxms.ui.eclipse.jobs.ConsoleJob;
+import org.netxms.ui.eclipse.objecttools.Activator;
 import org.netxms.ui.eclipse.objecttools.Messages;
 import org.netxms.ui.eclipse.shared.ConsoleSharedData;
+import org.netxms.ui.eclipse.tools.MessageDialogHelper;
 
 /**
  * File viewer
@@ -46,7 +51,11 @@ public class FileViewer extends ViewPart
        private long nodeId;
        private String remoteFileName;
        private File currentFile;
-       private StyledText textViewer;
+       private Text textViewer;
+       private final NXCSession session = (NXCSession)ConsoleSharedData.getSession();
+       private boolean follow;
+       private ConsoleJob job;
+       
 
        /* (non-Javadoc)
         * @see org.eclipse.ui.part.ViewPart#init(org.eclipse.ui.IViewSite)
@@ -62,7 +71,7 @@ public class FileViewer extends ViewPart
                        throw new PartInitException("Internal error"); //$NON-NLS-1$
                
                nodeId = Long.parseLong(parts[0]);
-               AbstractObject object = ((NXCSession)ConsoleSharedData.getSession()).findObjectById(nodeId);
+               AbstractObject object = session.findObjectById(nodeId);
                if ((object == null) || (object.getObjectClass() != AbstractObject.OBJECT_NODE))
                        throw new PartInitException(Messages.get().FileViewer_InvalidObjectID);
                
@@ -84,7 +93,7 @@ public class FileViewer extends ViewPart
        @Override
        public void createPartControl(Composite parent)
        {
-               textViewer = new StyledText(parent, SWT.H_SCROLL | SWT.V_SCROLL);
+               textViewer = new Text(parent, SWT.H_SCROLL | SWT.V_SCROLL);
                textViewer.setEditable(false);
                textViewer.setFont(JFaceResources.getTextFont());
        }
@@ -101,11 +110,73 @@ public class FileViewer extends ViewPart
        /**
         * @param file
         */
-       public void showFile(File file)
+       public void showFile(File file, boolean follow)
        {
                currentFile = file;
                textViewer.setText(loadFile(currentFile));
+               this.follow = follow;
+               if(follow){
+               job = new ConsoleJob("Download file updates from agent", null, Activator.PLUGIN_ID, null) {
+   
+                  private boolean continueWork = true;
+                  
+                  @Override
+                  protected void canceling() {
+                     continueWork = false;
+                  }
+                  
+            @Override
+            protected void runInternal(IProgressMonitor monitor) throws Exception
+            {
+               while(continueWork)
+               {
+                  final String s = session.waitForFileTail(remoteFileName, 3000);
+                  if(s!=null)
+                  {
+                     runInUIThread(new Runnable() {                  
+                        @Override
+                        public void run()
+                        {
+                           if(!textViewer.isDisposed())
+                           {
+                              textViewer.append(s); 
+                           }
+                        }
+                     }); 
+
+                  }                    
+               }
+            }
+   
+            @Override
+            protected String getErrorMessage()
+            {
+               return String.format(Messages.get().ObjectToolsDynamicMenu_DownloadError, remoteFileName, nodeId);
+            }
+               
+               };
+               job.start();
+               }
+      
        }
+   
+   @Override
+   public void dispose()
+   {
+      if(follow){
+         try
+         {
+            session.cancelFileMonitoring(nodeId, remoteFileName);
+         }
+         catch(Exception e)
+         {
+            e.printStackTrace();
+         }
+         job.cancel();
+      }
+      super.dispose();
+   }
+   
        
        /**
         * @param file
index 3f38fdf..6e8e252 100644 (file)
@@ -1,4 +1,4 @@
-/* 
+/*
 ** NetXMS - Network Management System
 ** NetXMS Foundation Library
 ** Copyright (C) 2003-2013 Victor Kirhenshtein
@@ -29,7 +29,7 @@
 static void (* s_fpWriteLog)(int, int, const TCHAR *) = NULL;
 static void (* s_fpSendTrap1)(UINT32, const TCHAR *, const char *, va_list) = NULL;
 static void (* s_fpSendTrap2)(UINT32, const TCHAR *, int, TCHAR **) = NULL;
-static bool (* s_fpSendFile)(void *, UINT32, const TCHAR *, long) = NULL;
+static bool (* s_fpSendFile)(void *, UINT32, const TCHAR *, long, long) = NULL;
 static bool (* s_fpPushData)(const TCHAR *, const TCHAR *, UINT32) = NULL;
 
 /**
@@ -38,7 +38,7 @@ static bool (* s_fpPushData)(const TCHAR *, const TCHAR *, UINT32) = NULL;
 void LIBNETXMS_EXPORTABLE InitSubAgentAPI(void (* writeLog)(int, int, const TCHAR *),
                                                                                                                void (* sendTrap1)(UINT32, const TCHAR *, const char *, va_list),
                                                                                                                void (* sendTrap2)(UINT32, const TCHAR *, int, TCHAR **),
-                                                                                                               bool (* sendFile)(void *, UINT32, const TCHAR *, long),
+                                                                                                               bool (* sendFile)(void *, UINT32, const TCHAR *, long, long),
                                                                                                                bool (* pushData)(const TCHAR *, const TCHAR *, UINT32))
 {
    s_fpWriteLog = writeLog;
@@ -253,11 +253,11 @@ BOOL LIBNETXMS_EXPORTABLE AgentGetParameterArgW(const TCHAR *param, int index, W
 /**
  * Send file to server
  */
-BOOL LIBNETXMS_EXPORTABLE AgentSendFileToServer(void *session, UINT32 requestId, const TCHAR *file, long offset)
+BOOL LIBNETXMS_EXPORTABLE AgentSendFileToServer(void *session, UINT32 requestId, const TCHAR *file, long offset, long sizeLimit)
 {
        if ((s_fpSendFile == NULL) || (session == NULL) || (file == NULL))
                return FALSE;
-       return s_fpSendFile(session, requestId, file, offset);
+       return s_fpSendFile(session, requestId, file, offset, sizeLimit);
 }
 
 /**
index 7e36008..fe7099a 100644 (file)
@@ -1,4 +1,4 @@
-/* 
+/*
 ** NetXMS - Network Management System
 ** NetXMS Foundation Library
 ** Copyright (C) 2003-2013 Victor Kirhenshtein
 #define close  _close
 #endif
 
-/**
- * Constants
- */
-#define FILE_BUFFER_SIZE      32768
-
 /**
  * Get symbolic name for message code
  */
@@ -323,10 +318,12 @@ TCHAR LIBNETXMS_EXPORTABLE *NXCPMessageCodeName(WORD wCode, TCHAR *pszBuffer)
                _T("CMD_QUERY_SUMMARY_TABLE"),
       _T("CMD_SHUTDOWN"),
       _T("CMD_SNMP_TRAP"),
-      _T("CMD_GET_SUBNET_ADDRESS_MAP")
+      _T("CMD_GET_SUBNET_ADDRESS_MAP"),
+      _T("CMD_FILE_MONITORING"),
+      _T("CMD_CANCEL_FILE_MONITORING")
    };
 
-   if ((wCode >= CMD_LOGIN) && (wCode <= CMD_GET_SUBNET_ADDRESS_MAP))
+   if ((wCode >= CMD_LOGIN) && (wCode <= CMD_CANCEL_FILE_MONITORING))
       _tcscpy(pszBuffer, pszMsgNames[wCode - CMD_LOGIN]);
    else
       _sntprintf(pszBuffer, 64, _T("CMD_0x%04X"), wCode);
@@ -538,11 +535,12 @@ CSCP_MESSAGE LIBNETXMS_EXPORTABLE *CreateRawNXCPMessage(WORD wCode, UINT32 dwId,
  * Send file over CSCP
  */
 BOOL LIBNETXMS_EXPORTABLE SendFileOverNXCP(SOCKET hSocket, UINT32 dwId, const TCHAR *pszFile,
-                                           NXCPEncryptionContext *pCtx, long offset,
+                                           NXCPEncryptionContext *pCtx, long offset, long sizeLimit,
                                                                                                                 void (* progressCallback)(INT64, void *), void *cbArg,
                                                                                                                 MUTEX mutex)
 {
    int hFile, iBytes;
+   long fileSize, sendFileSize;
        INT64 bytesTransferred = 0;
    UINT32 dwPadding;
    BOOL bResult = FALSE;
@@ -552,6 +550,19 @@ BOOL LIBNETXMS_EXPORTABLE SendFileOverNXCP(SOCKET hSocket, UINT32 dwId, const TC
    hFile = _topen(pszFile, O_RDONLY | O_BINARY);
    if (hFile != -1)
    {
+      if(sizeLimit > 0)
+      {
+         #ifdef _WIN32
+                  struct _stat fs;
+         #else
+                  struct stat st;
+         #endif
+         //debug
+         fstat(hFile, &st);
+         fileSize = st.st_size;
+         sendFileSize = (fileSize - offset) > fileSize ? (0 - offset) : (fileSize - offset);
+         offset = sendFileSize > sizeLimit ?  - sizeLimit : offset;
+      }
                if (lseek(hFile, offset, offset < 0 ? SEEK_END : SEEK_SET) != -1)
                {
                        // Allocate message and prepare it's header
index 5e204e3..d67b7b3 100644 (file)
@@ -190,7 +190,7 @@ public:
    CSCP_MESSAGE *WaitForRawMessage(WORD wCode, UINT32 dwId, UINT32 dwTimeOut = 0);
    UINT32 WaitForRCC(UINT32 dwRqId, UINT32 dwTimeOut = 0);
    UINT32 CreateRqId(void) { return m_dwMsgId++; }
-   UINT32 SendFile(UINT32 dwRqId, TCHAR *pszFileName);
+   UINT32 SendFile(UINT32 dwRqId, TCHAR *pszFileName, long sizeLimit);
    UINT32 SimpleCommand(WORD wCmd);
 
    void callEventHandler(UINT32 dwEvent, UINT32 dwCode, void *pArg);
index d68f5f5..309193c 100644 (file)
@@ -1,4 +1,4 @@
-/* 
+/*
 ** NetXMS - Network Management System
 ** Client Library
 ** Copyright (C) 2003-2010 Victor Kirhenshtein
@@ -172,7 +172,7 @@ UINT32 LIBNXCL_EXPORTABLE NXCInstallPackage(NXC_SESSION hSession, NXC_PACKAGE_IN
    // If everything is OK, send package file to server
    if (dwResult == RCC_SUCCESS)
    {
-      dwResult = ((NXCL_Session *)hSession)->SendFile(dwRqId, pszFullPkgPath);
+      dwResult = ((NXCL_Session *)hSession)->SendFile(dwRqId, pszFullPkgPath, 0);
       if (dwResult == RCC_SUCCESS)
       {
          // Wait for final confirmation
index 1085400..d6772ba 100644 (file)
@@ -848,9 +848,9 @@ UINT32 NXCL_Session::LoadUserDB(void)
 // Send file to server
 //
 
-UINT32 NXCL_Session::SendFile(UINT32 dwRqId, TCHAR *pszFileName)
+UINT32 NXCL_Session::SendFile(UINT32 dwRqId, TCHAR *pszFileName, long sizeLimit)
 {
-   return SendFileOverNXCP(m_hSocket, dwRqId, pszFileName, m_pCtx, 0, NULL, NULL, m_mutexSendMsg) ? RCC_SUCCESS : RCC_IO_ERROR;
+   return SendFileOverNXCP(m_hSocket, dwRqId, pszFileName, m_pCtx, 0, sizeLimit, NULL, NULL, m_mutexSendMsg) ? RCC_SUCCESS : RCC_IO_ERROR;
 }
 
 
index 6ff4951..e36305b 100644 (file)
@@ -12,7 +12,7 @@ libnxcore_la_SOURCES =  accesspoint.cpp acl.cpp actions.cpp admin.cpp \
                        dcobject.cpp dcst.cpp dctable.cpp dctarget.cpp \
                        dctcolumn.cpp dctthreshold.cpp debug.cpp \
                        download_job.cpp ef.cpp email.cpp entirenet.cpp \
-                       epp.cpp events.cpp evproc.cpp fdb.cpp graph.cpp hk.cpp \
+                       epp.cpp events.cpp evproc.cpp fdb.cpp filemonitoring.cpp graph.cpp hk.cpp \
                        id.cpp import.cpp index.cpp interface.cpp isc.cpp job.cpp \
                        jobmgr.cpp jobqueue.cpp layer2.cpp lln.cpp lldp.cpp \
                        locks.cpp logfilter.cpp loghandle.cpp logs.cpp \
index bb048e5..bc56b0e 100644 (file)
@@ -1,4 +1,4 @@
-/* 
+/*
 ** NetXMS - Network Management System
 ** Copyright (C) 2003-2012 Victor Kirhenshtein
 **
@@ -92,7 +92,7 @@ void AgentConnectionEx::onTrap(CSCPMessage *pMsg)
                                                 pszArgList[20], pszArgList[21], pszArgList[22], pszArgList[23],
                                                 pszArgList[24], pszArgList[25], pszArgList[26], pszArgList[27],
                                                 pszArgList[28], pszArgList[29], pszArgList[30], pszArgList[31]);
-             
+
                        // Cleanup
                        for(i = 0; i < iNumArgs; i++)
                                free(pszArgList[i]);
@@ -201,6 +201,35 @@ void AgentConnectionEx::printMsg(const TCHAR *format, ...)
    va_end(args);
 }
 
+/*
+ * Recieve file monitoring information and resend to all required user sessions
+ */
+void AgentConnectionEx::onFileMonitoringData(CSCPMessage *pMsg)
+{
+   UINT32 rqId, rcc;
+       TCHAR remoteFile[MAX_PATH], localFile[MAX_PATH];
+       BYTE *pConfig;
+   UINT32 dwSize;
+
+   rqId = pMsg->GetId();
+       NetObj *object = NULL;
+       if (m_nodeId != 0)
+               object = (Node *)FindObjectById(m_nodeId, OBJECT_NODE);
+       if (object != NULL)
+       {
+      pMsg->GetVariableStr(VID_FILE_NAME, remoteFile, MAX_PATH);
+      ObjectArray<ClientSession>* result = g_monitoringList.findClientByFNameAndNodID(remoteFile, object->Id());
+      for(int i = 0; i < result->size(); i++)
+      {
+         result->get(i)->sendMessage(pMsg);
+      }
+       }
+       else
+       {
+               g_monitoringList.removeDisconectedNode(m_nodeId);
+       }
+}
+
 /**
  * Deploy policy to agent
  */
index 60b73cf..45b31e4 100644 (file)
@@ -1,4 +1,4 @@
-/* 
+/*
 ** NetXMS - Network Management System
 ** Copyright (C) 2003-2013 Victor Kirhenshtein
 **
@@ -25,7 +25,7 @@
 /**
  * Constructor for download job
  */
-FileDownloadJob::FileDownloadJob(Node *node, const TCHAR *remoteFile, ClientSession *session, UINT32 requestId)
+FileDownloadJob::FileDownloadJob(Node *node, const TCHAR *remoteFile, UINT32 maxFileSize, bool follow, ClientSession *session, UINT32 requestId)
                 : ServerJob(_T("DOWNLOAD_FILE"), _T("Download file"), node->Id(), session->getUserId(), false)
 {
        m_session = session;
@@ -49,6 +49,9 @@ FileDownloadJob::FileDownloadJob(Node *node, const TCHAR *remoteFile, ClientSess
        m_info = _tcsdup(buffer);
 
        setAutoCancelDelay(60);
+
+       m_maxFileSize = maxFileSize;
+       m_follow = follow;
 }
 
 /**
@@ -85,6 +88,16 @@ bool FileDownloadJob::run()
        UINT32 rcc = 0xFFFFFFFF;
        bool success = false;
 
+   MONITORED_FILE * newFile = new MONITORED_FILE();
+   _tcscpy(newFile->fileName, m_remoteFile);
+   newFile->nodeID = m_node->Id();
+   newFile->session = m_session;
+
+   if(g_monitoringList.checkDublicate(newFile))
+   {
+      m_follow = false;
+   }
+
        conn = m_node->createAgentConnection();
        if (conn != NULL)
        {
@@ -130,6 +143,11 @@ bool FileDownloadJob::run()
                                        }
                                }
 
+            //default - get parameters
+
+            msg.SetVariable(VID_FILE_SIZE_LIMIT, m_maxFileSize);
+            msg.SetVariable(VID_FILE_FOLLOW, (INT16)m_follow);
+
                                response = conn->customRequest(&msg, m_localFile, appendFile, progressCallback, this);
                                if (response != NULL)
                                {
@@ -182,7 +200,11 @@ bool FileDownloadJob::run()
        {
           response.SetVariable(VID_RCC, RCC_SUCCESS);
                m_session->sendMessage(&response);
-               m_session->sendFile(m_localFile, m_requestId);
+               m_session->sendFile(m_localFile, m_requestId, m_maxFileSize);
+               if(m_follow)
+               {
+         g_monitoringList.addMonitoringFile(newFile);
+               }
        }
        else
        {
diff --git a/src/server/core/filemonitoring.cpp b/src/server/core/filemonitoring.cpp
new file mode 100644 (file)
index 0000000..c0e3692
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+** NetXMS multiplatform core agent
+** Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 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: logmonitoring.cpp
+**
+**/
+
+#include "nms_core.h"
+
+FileMonitoringList g_monitoringList;
+
+FileMonitoringList::FileMonitoringList()
+{
+   m_mutex = MutexCreate();
+   m_monitoredFiles.setOwner(true);
+};
+
+FileMonitoringList::~FileMonitoringList()
+{
+   MutexDestroy(m_mutex);
+};
+
+void FileMonitoringList::addMonitoringFile(MONITORED_FILE *fileForAdd)
+{
+   lock();
+   fileForAdd->session->incRefCount();
+   m_monitoredFiles.add(fileForAdd);
+   unlock();
+};
+
+bool FileMonitoringList::checkDublicate(MONITORED_FILE *fileForAdd)
+{
+   bool result = false;
+   lock();
+   for(int i = 0; i < m_monitoredFiles.size(); i++)
+   {
+      m_monitoredFile = m_monitoredFiles.get(i);
+      if(_tcscmp(m_monitoredFile->fileName, fileForAdd->fileName) == 0
+         && m_monitoredFile->nodeID == fileForAdd->nodeID
+         && m_monitoredFile->session->getUserId() == fileForAdd->session->getUserId())
+      {
+         result=true;
+      }
+   }
+   unlock();
+   return result;
+};
+
+ObjectArray<ClientSession>* FileMonitoringList::findClientByFNameAndNodID(const TCHAR *fileName, UINT32 nodeID)
+{
+   lock();
+   ObjectArray<ClientSession> *result = new ObjectArray<ClientSession>;
+   for(int i = 0; i < m_monitoredFiles.size(); i++)
+   {
+      m_monitoredFile = m_monitoredFiles.get(i);
+      if(_tcscmp(m_monitoredFile->fileName, fileName) == 0 && m_monitoredFile->nodeID == nodeID)
+      {
+         result->add(m_monitoredFile->session);
+      }
+   }
+   unlock();
+   return result;
+};
+
+bool FileMonitoringList::removeMonitoringFile(MONITORED_FILE *fileForRemove)
+{
+   lock();
+   bool deleted = false;
+   for(int i = 0; i < m_monitoredFiles.size(); i++)
+   {
+      m_monitoredFile = m_monitoredFiles.get(i);
+      if(_tcscmp(m_monitoredFile->fileName, fileForRemove->fileName) == 0 &&
+         m_monitoredFile->nodeID == fileForRemove->nodeID &&
+         m_monitoredFile->session == fileForRemove->session)
+      {
+         fileForRemove->session->decRefCount();
+         m_monitoredFiles.remove(i);
+         deleted = true;
+         break;
+      }
+   }
+   unlock();
+   return deleted;
+};
+
+void FileMonitoringList::removeDisconectedNode(UINT32 nodeId)
+{
+   lock();
+   bool deleted = false;
+   for(int i = 0; i < m_monitoredFiles.size(); i++)
+   {
+      m_monitoredFile = m_monitoredFiles.get(i);
+      if(m_monitoredFile->nodeID == nodeId)
+      {
+         m_monitoredFile->session->decRefCount();
+         m_monitoredFiles.remove(i);
+      }
+   }
+   unlock();
+};
+
+void FileMonitoringList::lock()
+{
+   MutexLock(m_mutex);
+};
+
+void FileMonitoringList::unlock()
+{
+   MutexUnlock(m_mutex);
+};
index 50fcb07..b1ff01b 100644 (file)
@@ -144,6 +144,7 @@ DEFINE_THREAD_STARTER(sendSyslog)
 DEFINE_THREAD_STARTER(createObject)
 DEFINE_THREAD_STARTER(getServerFile)
 DEFINE_THREAD_STARTER(getAgentFile)
+DEFINE_THREAD_STARTER(cancelFileMonitoring)
 DEFINE_THREAD_STARTER(queryServerLog)
 DEFINE_THREAD_STARTER(getServerLogQueryData)
 DEFINE_THREAD_STARTER(executeAction)
@@ -1187,6 +1188,9 @@ void ClientSession::processingThread()
                        case CMD_GET_AGENT_FILE:
                                CALL_IN_NEW_THREAD(getAgentFile, pMsg);
                                break;
+         case CMD_CANCEL_FILE_MONITORING:
+                               CALL_IN_NEW_THREAD(cancelFileMonitoring, pMsg);
+                               break;
                        case CMD_TEST_DCI_TRANSFORMATION:
                                testDCITransformation(pMsg);
                                break;
@@ -1493,9 +1497,9 @@ void ClientSession::sendRawMessage(CSCP_MESSAGE *msg)
 /**
  * Send file to client
  */
-BOOL ClientSession::sendFile(const TCHAR *file, UINT32 dwRqId)
+BOOL ClientSession::sendFile(const TCHAR *file, UINT32 dwRqId, long sizeLimit)
 {
-       return SendFileOverNXCP(m_hSocket, dwRqId, file, m_pCtx, 0, NULL, NULL, m_mutexSocketWrite);
+       return SendFileOverNXCP(m_hSocket, dwRqId, file, m_pCtx, 0, sizeLimit, NULL, NULL, m_mutexSocketWrite);
 }
 
 /**
@@ -4106,7 +4110,7 @@ void ClientSession::sendMib(CSCPMessage *request)
    // Send compiled MIB file
    _tcscpy(szBuffer, g_szDataDir);
    _tcscat(szBuffer, DFILE_COMPILED_MIB);
-       sendFile(szBuffer, request->GetId());
+       sendFile(szBuffer, request->GetId(), 0);
 }
 
 
@@ -10108,7 +10112,7 @@ void ClientSession::getServerFile(CSCPMessage *pRequest)
                if (_taccess(fname, 0) == 0)
                {
                        debugPrintf(5, _T("Sending file %s"), name);
-                       if (SendFileOverNXCP(m_hSocket, pRequest->GetId(), fname, m_pCtx, 0, NULL, NULL, m_mutexSocketWrite))
+                       if (SendFileOverNXCP(m_hSocket, pRequest->GetId(), fname, m_pCtx, 0, 0, NULL, NULL, m_mutexSocketWrite))
                        {
                                debugPrintf(5, _T("File %s was succesfully sent"), name);
                      msg.SetVariable(VID_RCC, RCC_SUCCESS);
@@ -10153,7 +10157,8 @@ void ClientSession::getAgentFile(CSCPMessage *request)
                        {
                                request->GetVariableStr(VID_FILE_NAME, remoteFile, MAX_PATH);
                                FileDownloadJob::buildServerFileName(object->Id(), remoteFile, localFile, MAX_PATH);
-                               FileDownloadJob *job = new FileDownloadJob((Node *)object, remoteFile, this, request->GetId());
+                               bool follow = request->GetVariableShort(VID_FILE_FOLLOW);
+                               FileDownloadJob *job = new FileDownloadJob((Node *)object, remoteFile, request->GetVariableLong(VID_FILE_SIZE_LIMIT), follow, this, request->GetId());
                                msg.SetVariable(VID_RCC, AddJob(job) ? RCC_SUCCESS : RCC_INTERNAL_ERROR);
                        }
                        else
@@ -10174,6 +10179,72 @@ void ClientSession::getAgentFile(CSCPMessage *request)
    sendMessage(&msg);
 }
 
+/**
+ * Get file from agent
+ */
+void ClientSession::cancelFileMonitoring(CSCPMessage *request)
+{
+   CSCPMessage msg;
+   CSCPMessage* response;
+       TCHAR remoteFile[MAX_PATH];
+       UINT32 rcc = 0xFFFFFFFF;
+
+   msg.SetCode(CMD_REQUEST_COMPLETED);
+   msg.SetId(request->GetId());
+
+       NetObj *object = FindObjectById(request->GetVariableLong(VID_OBJECT_ID));
+       if (object != NULL)
+       {
+      if (object->Type() == OBJECT_NODE)
+      {
+         request->GetVariableStr(VID_FILE_NAME, remoteFile, MAX_PATH);
+
+         MONITORED_FILE * newFile = new MONITORED_FILE();
+         _tcscpy(newFile->fileName, remoteFile);
+         newFile->nodeID = object->Id();
+         newFile->session = this;
+         g_monitoringList.removeMonitoringFile(newFile);
+         delete newFile;
+
+         Node *node = (Node *)object;
+         node->incRefCount();
+         AgentConnection *conn = node->createAgentConnection();
+         if(conn != NULL)
+         {
+            request->SetId(conn->generateRequestId());
+            response = conn->customRequest(request);
+            if (response != NULL)
+            {
+               rcc = response->GetVariableLong(VID_RCC);
+               msg.SetVariable(VID_RCC, rcc);
+               debugPrintf(6, _T("File monitoring canceled sucessfully"));
+            }
+            else
+            {
+               msg.SetVariable(VID_RCC, RCC_INTERNAL_ERROR);
+            }
+            delete response;
+            delete conn;
+         }
+         else
+         {
+            msg.SetVariable(VID_RCC, RCC_INTERNAL_ERROR);
+            debugPrintf(6, _T("Connection with node have been lost"));
+         }
+         node->decRefCount();
+      }
+      else
+      {
+         msg.SetVariable(VID_RCC, RCC_INCOMPATIBLE_OPERATION);
+      }
+       }
+       else
+       {
+               msg.SetVariable(VID_RCC, RCC_INVALID_OBJECT_ID);
+       }
+   sendMessage(&msg);
+}
+
 /**
  * Test DCI transformation script
  */
@@ -10912,7 +10983,7 @@ void ClientSession::sendLibraryImage(CSCPMessage *request)
        sendMessage(&msg);
 
        if (rcc == RCC_SUCCESS)
-               sendFile(absFileName, request->GetId());
+               sendFile(absFileName, request->GetId(), 0);
 }
 
 void ClientSession::onLibraryImageChange(uuid_t *guid, bool removed)
@@ -11911,7 +11982,7 @@ void ClientSession::renderReport(CSCPMessage *request)
 
        if (ret == 0)
        {
-               sendFile(outputFileName, request->GetId());
+               sendFile(outputFileName, request->GetId(), 0);
        }
 }
 
index 6ce4af5..b1604ce 100644 (file)
@@ -282,6 +282,7 @@ protected:
    virtual void printMsg(const TCHAR *format, ...);
    virtual void onTrap(CSCPMessage *msg);
    virtual void onDataPush(CSCPMessage *msg);
+   virtual void onFileMonitoringData(CSCPMessage *msg);
 
 public:
    AgentConnectionEx(UINT32 nodeId, UINT32 ipAddr, WORD port = AGENT_LISTEN_PORT, int authMethod = AUTH_NONE, const TCHAR *secret = NULL) :
@@ -438,6 +439,7 @@ private:
        DECLARE_THREAD_STARTER(createObject)
        DECLARE_THREAD_STARTER(getServerFile)
        DECLARE_THREAD_STARTER(getAgentFile)
+       DECLARE_THREAD_STARTER(cancelFileMonitoring)
        DECLARE_THREAD_STARTER(queryServerLog)
        DECLARE_THREAD_STARTER(getServerLogQueryData)
        DECLARE_THREAD_STARTER(executeAction)
@@ -599,6 +601,7 @@ private:
        void getConfigCLOB(CSCPMessage *pRequest);
        void registerAgent(CSCPMessage *pRequest);
        void getServerFile(CSCPMessage *pRequest);
+       void cancelFileMonitoring(CSCPMessage *request);
        void getAgentFile(CSCPMessage *pRequest);
        void testDCITransformation(CSCPMessage *pRequest);
        void sendJobList(UINT32 dwRqId);
@@ -666,7 +669,7 @@ public:
    void sendMessage(CSCPMessage *pMsg);
    void sendRawMessage(CSCP_MESSAGE *pMsg);
    void sendPollerMsg(UINT32 dwRqId, const TCHAR *pszMsg);
-       BOOL sendFile(const TCHAR *file, UINT32 dwRqId);
+       BOOL sendFile(const TCHAR *file, UINT32 dwRqId, long sizeLimit);
 
    UINT32 getIndex() { return m_dwIndex; }
    void setIndex(UINT32 dwIndex) { if (m_dwIndex == INVALID_INDEX) m_dwIndex = dwIndex; }
@@ -917,6 +920,37 @@ GRAPH_ACL_AND_ID IsGraphNameExists(const TCHAR *graphName);
 bool SendXMPPMessage(const TCHAR *rcpt, const TCHAR *message);
 #endif
 
+/**
+ * File monitoring
+ */
+struct MONITORED_FILE
+{
+   TCHAR fileName[MAX_PATH];
+   ClientSession *session;
+   UINT32 nodeID;
+};
+
+class FileMonitoringList
+{
+private:
+   MUTEX m_mutex;
+   ObjectArray<MONITORED_FILE>  m_monitoredFiles;
+   MONITORED_FILE* m_monitoredFile;
+
+public:
+   FileMonitoringList();
+   ~FileMonitoringList();
+   void addMonitoringFile(MONITORED_FILE *fileForAdd);
+   bool checkDublicate(MONITORED_FILE *fileForAdd);
+   ObjectArray<ClientSession>* findClientByFNameAndNodID(const TCHAR *fileName, UINT32 nodeID);
+   bool removeMonitoringFile(MONITORED_FILE *fileForRemove);
+   void removeDisconectedNode(UINT32 nodeId);
+
+private:
+   void lock();
+   void unlock();
+};
+
 /**
  * Global variables
  */
@@ -961,5 +995,6 @@ extern Queue *g_pLazyRequestQueue;
 extern Queue *g_pIDataInsertQueue;
 
 extern int NXCORE_EXPORTABLE g_nDBSyntax;
+extern FileMonitoringList g_monitoringList;
 
 #endif   /* _nms_core_h_ */
index ed78367..9a7ebea 100644 (file)
@@ -1,4 +1,4 @@
-/* 
+/*
 ** NetXMS - Network Management System
 ** Copyright (C) 2003-2009 Victor Kirhenshtein
 **
@@ -191,6 +191,8 @@ private:
        TCHAR *m_info;
        INT64 m_fileSize;
        SOCKET m_socket;
+       UINT32 m_maxFileSize;
+       bool m_follow;
 
 protected:
        virtual bool run();
@@ -200,7 +202,7 @@ protected:
        static void progressCallback(size_t size, void *arg);
 
 public:
-       FileDownloadJob(Node *node, const TCHAR *remoteName, ClientSession *session, UINT32 requestId);
+       FileDownloadJob(Node *node, const TCHAR *remoteName, UINT32 maxFileSize, bool follow, ClientSession *session, UINT32 requestId);
        virtual ~FileDownloadJob();
 
        static TCHAR *buildServerFileName(UINT32 nodeId, const TCHAR *remoteFile, TCHAR *buffer, size_t bufferSize);
index 0046346..195c71a 100644 (file)
@@ -467,6 +467,7 @@ protected:
    virtual void printMsg(const TCHAR *format, ...);
    virtual void onTrap(CSCPMessage *pMsg);
        virtual void onDataPush(CSCPMessage *msg);
+       virtual void onFileMonitoringData(CSCPMessage *msg);
        virtual bool processCustomMessage(CSCPMessage *pMsg);
        virtual void onFileDownload(BOOL success);
 
index fe35429..a5a5abc 100644 (file)
@@ -299,6 +299,10 @@ void AgentConnection::receiverThread()
                                case CMD_REQUEST_COMPLETED:
                                        m_pMsgWaitQueue->put(pMsg);
                                        break;
+            case CMD_FILE_MONITORING:
+               onFileMonitoringData(pMsg);
+                                       delete pMsg;
+               break;
                                default:
                                        if (processCustomMessage(pMsg))
                                                delete pMsg;
@@ -523,7 +527,7 @@ void AgentConnection::disconnect()
                m_hCurrFile = -1;
                onFileDownload(FALSE);
        }
-       
+
    if (m_hSocket != -1)
    {
       shutdown(m_hSocket, SHUT_RDWR);
@@ -847,6 +851,14 @@ void AgentConnection::onDataPush(CSCPMessage *pMsg)
 {
 }
 
+/**
+ * Monitoring data handler. Should be overriden in derived classes to implement
+ * actual monitoring data processing. Default implementation do nothing.
+ */
+void AgentConnection::onFileMonitoringData(CSCPMessage *pMsg)
+{
+}
+
 /**
  * Custom message handler
  * If returns true, message considered as processed and will not be placed in wait queue
@@ -1064,7 +1076,7 @@ UINT32 AgentConnection::uploadFile(const TCHAR *localFile, const TCHAR *destinat
    {
                m_fileUploadInProgress = true;
                NXCPEncryptionContext *ctx = acquireEncryptionContext();
-      if (SendFileOverNXCP(m_hSocket, dwRqId, localFile, ctx, 0, progressCallback, cbArg, m_mutexSocketWrite))
+      if (SendFileOverNXCP(m_hSocket, dwRqId, localFile, ctx, 0, 0, progressCallback, cbArg, m_mutexSocketWrite))
          dwResult = waitForRCC(dwRqId, m_dwCommandTimeout);
       else
          dwResult = ERR_IO_FAILURE;
index a280eae..59c441a 100644 (file)
@@ -399,7 +399,11 @@ public class ObjectToolsDynamicMenu extends ContributionItem implements IWorkben
        private void executeFileDownload(final AbstractNode node, final ObjectTool tool)
        {
                final NXCSession session = (NXCSession)ConsoleSharedData.getSession();
-               final String fileName = substituteMacros(tool.getData(), node);
+               String[] parameters = tool.getData().split("\u007F");
+               
+               final String fileName = substituteMacros(parameters[0], node);
+               final int maxFileSize = Integer.parseInt(parameters[1]);
+               final boolean follow = parameters[2].equals("true") ? true : false;
                
                ConsoleJob job = new ConsoleJob(Messages.get().ObjectToolsDynamicMenu_DownloadFromAgent, null, Activator.PLUGIN_ID, null) {
                        @Override
@@ -411,7 +415,7 @@ public class ObjectToolsDynamicMenu extends ContributionItem implements IWorkben
                        @Override
                        protected void runInternal(IProgressMonitor monitor) throws Exception
                        {
-                               final File file = session.downloadFileFromAgent(node.getObjectId(), fileName);
+                               final File file = session.downloadFileFromAgent(node.getObjectId(), fileName, maxFileSize, follow); 
                                runInUIThread(new Runnable() {
                                        @Override
                                        public void run()
@@ -421,7 +425,7 @@ public class ObjectToolsDynamicMenu extends ContributionItem implements IWorkben
                                                {
                                                        String secondaryId = Long.toString(node.getObjectId()) + "&" + URLEncoder.encode(fileName, "UTF-8"); //$NON-NLS-1$ //$NON-NLS-2$
                                                        FileViewer view = (FileViewer)window.getActivePage().showView(FileViewer.ID, secondaryId, IWorkbenchPage.VIEW_ACTIVATE);
-                                                       view.showFile(file);
+                                                       view.showFile(file, follow);
                                                }
                                                catch(Exception e)
                                                {
index 4eca2ab..4f481f5 100644 (file)
@@ -32,6 +32,7 @@ import org.eclipse.ui.dialogs.PropertyPage;
 import org.netxms.client.objecttools.ObjectTool;
 import org.netxms.client.objecttools.ObjectToolDetails;
 import org.netxms.ui.eclipse.objecttools.Messages;
+import org.netxms.ui.eclipse.tools.MessageDialogHelper;
 import org.netxms.ui.eclipse.tools.WidgetHelper;
 import org.netxms.ui.eclipse.widgets.LabeledText;
 
@@ -44,10 +45,12 @@ public class General extends PropertyPage
        private LabeledText textName;
        private LabeledText textDescription;
        private LabeledText textData;
+       private LabeledText maxFileLenght;
        private LabeledText textParameter;
        private LabeledText textRegexp;
        private Button checkOutput;
        private Button checkConfirmation;
+       private Button follow;
        private LabeledText textConfirmation;
        private Button radioIndexOID;
        private Button radioIndexValue;
@@ -119,7 +122,23 @@ public class General extends PropertyPage
                                textData.setLabel(Messages.get().General_URL);
                                break;
                        case ObjectTool.TYPE_FILE_DOWNLOAD:
+                          String[] parameters = objectTool.getData().split("\u007F"); //$NON-NLS-1$
+                          
                                textData.setLabel(Messages.get().General_RemoteFileName);
+                               textData.setText((parameters.length > 0) ? parameters[0] : ""); //$NON-NLS-1$
+                               
+                     maxFileLenght  = new LabeledText(dialogArea, SWT.NONE);
+                     maxFileLenght.setLayoutData(gd);
+                               maxFileLenght.setLabel("Maximum file download size");
+                               maxFileLenght.setText((parameters.length > 1) ? parameters[1] : ""); //$NON-NLS-1$
+                                                       
+                               follow = new Button(dialogArea, SWT.CHECK);
+                               follow.setText("Get file updates");
+                               if(parameters.length > 2) //$NON-NLS-1$
+                               {
+                                  follow.setSelection( parameters[2].equals("true") ? true : false);  //$NON-NLS-1$
+                               }       
+                               
                                break;
                        case ObjectTool.TYPE_TABLE_SNMP:
                                textData.setLabel(Messages.get().General_Title);
@@ -241,7 +260,15 @@ public class General extends PropertyPage
                }
                else
                {
-                       objectTool.setData(textData.getText());
+                  if(objectTool.getType() == ObjectTool.TYPE_FILE_DOWNLOAD)
+               {
+                     String tmp = maxFileLenght.getText() == "" ? "0" : maxFileLenght.getText(); //$NON-NLS-1$ //$NON-NLS-2$
+                     objectTool.setData(textData.getText() + "\u007F" + tmp + "\u007F" + follow.getSelection()); //$NON-NLS-1$ //$NON-NLS-2$
+               }
+                  else
+                  {
+                     objectTool.setData(textData.getText());
+                  }
                }
                
                if (checkConfirmation.getSelection())
@@ -296,6 +323,19 @@ public class General extends PropertyPage
        @Override
        public boolean performOk()
        {
+          if(objectTool.getType() == ObjectTool.TYPE_FILE_DOWNLOAD && maxFileLenght.getText() != "")
+          {
+             try
+             {
+                int n = Integer.parseInt(maxFileLenght.getText());
+             }
+             catch(NumberFormatException e)
+             {
+                MessageDialogHelper.openWarning(getShell(), Messages.get().EditColumnDialog_Warning, "As maximum lenght should be given integre value in bytes.");
+                return false;
+             }
+          }
+          
                applyChanges(false);
                return true;
        }
index 7a41ceb..b54b711 100644 (file)
@@ -25,16 +25,21 @@ import java.io.UnsupportedEncodingException;
 import java.net.URLDecoder;
 import java.util.Arrays;
 import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.swt.SWT;
-import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Text;
+import org.eclipse.swt.widgets.Composite;
 import org.eclipse.ui.IViewSite;
 import org.eclipse.ui.PartInitException;
 import org.eclipse.ui.part.ViewPart;
+import org.netxms.client.NXCException;
 import org.netxms.client.NXCSession;
 import org.netxms.client.objects.AbstractObject;
+import org.netxms.ui.eclipse.jobs.ConsoleJob;
+import org.netxms.ui.eclipse.objecttools.Activator;
 import org.netxms.ui.eclipse.objecttools.Messages;
 import org.netxms.ui.eclipse.shared.ConsoleSharedData;
+import org.netxms.ui.eclipse.tools.MessageDialogHelper;
 
 /**
  * File viewer
@@ -47,6 +52,10 @@ public class FileViewer extends ViewPart
        private String remoteFileName;
        private File currentFile;
        private Text textViewer;
+       private final NXCSession session = (NXCSession)ConsoleSharedData.getSession();
+       private boolean follow;
+       private ConsoleJob job;
+       
 
        /* (non-Javadoc)
         * @see org.eclipse.ui.part.ViewPart#init(org.eclipse.ui.IViewSite)
@@ -62,7 +71,7 @@ public class FileViewer extends ViewPart
                        throw new PartInitException("Internal error"); //$NON-NLS-1$
                
                nodeId = Long.parseLong(parts[0]);
-               AbstractObject object = ((NXCSession)ConsoleSharedData.getSession()).findObjectById(nodeId);
+               AbstractObject object = session.findObjectById(nodeId);
                if ((object == null) || (object.getObjectClass() != AbstractObject.OBJECT_NODE))
                        throw new PartInitException(Messages.get().FileViewer_InvalidObjectID);
                
@@ -101,11 +110,73 @@ public class FileViewer extends ViewPart
        /**
         * @param file
         */
-       public void showFile(File file)
+       public void showFile(File file, boolean follow)
        {
                currentFile = file;
                textViewer.setText(loadFile(currentFile));
+               this.follow = follow;
+               if(follow){
+               job = new ConsoleJob("Download file updates from agent", null, Activator.PLUGIN_ID, null) {
+   
+                  private boolean continueWork = true;
+                  
+                  @Override
+                  protected void canceling() {
+                     continueWork = false;
+                  }
+                  
+            @Override
+            protected void runInternal(IProgressMonitor monitor) throws Exception
+            {
+               while(continueWork)
+               {
+                  final String s = session.waitForFileTail(remoteFileName, 3000);
+                  if(s!=null)
+                  {
+                     runInUIThread(new Runnable() {                  
+                        @Override
+                        public void run()
+                        {
+                           if(!textViewer.isDisposed())
+                           {
+                              textViewer.append(s); 
+                           }
+                        }
+                     }); 
+
+                  }                    
+               }
+            }
+   
+            @Override
+            protected String getErrorMessage()
+            {
+               return String.format(Messages.get().ObjectToolsDynamicMenu_DownloadError, remoteFileName, nodeId);
+            }
+               
+               };
+               job.start();
+               }
+      
        }
+   
+   @Override
+   public void dispose()
+   {
+      if(follow){
+         try
+         {
+            session.cancelFileMonitoring(nodeId, remoteFileName);
+         }
+         catch(Exception e)
+         {
+            e.printStackTrace();
+         }
+         job.cancel();
+      }
+      super.dispose();
+   }
+   
        
        /**
         * @param file