implemented single housekeeping thread for all message wait queues
[public/netxms.git] / src / server / tools / nxupload / nxupload.cpp
CommitLineData
5039dede
AK
1/*
2** nxupload - command line tool used to upload files to NetXMS agent
c11eee9b 3** Copyright (C) 2004-2015 Victor Kirhenshtein
5039dede
AK
4**
5** This program is free software; you can redistribute it and/or modify
6** it under the terms of the GNU General Public License as published by
7** the Free Software Foundation; either version 2 of the License, or
8** (at your option) any later version.
9**
10** This program is distributed in the hope that it will be useful,
11** but WITHOUT ANY WARRANTY; without even the implied warranty of
12** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13** GNU General Public License for more details.
14**
15** You should have received a copy of the GNU General Public License
16** along with this program; if not, write to the Free Software
17** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18**
bf6fb6c3 19** File: nxupload.cpp
5039dede
AK
20**
21**/
22
23#include <nms_common.h>
24#include <nms_agent.h>
25#include <nms_util.h>
5039dede
AK
26#include <nxsrvapi.h>
27
28#ifndef _WIN32
29#include <netdb.h>
30#endif
31
c11eee9b
VK
32/**
33 * Do agent upgrade
34 */
5039dede
AK
35static int UpgradeAgent(AgentConnection &conn, TCHAR *pszPkgName, BOOL bVerbose, RSA *pServerKey)
36{
967893bb 37 UINT32 dwError;
5039dede
AK
38 int i;
39 BOOL bConnected = FALSE;
40
4687826e 41 dwError = conn.startUpgrade(pszPkgName);
5039dede
AK
42 if (dwError == ERR_SUCCESS)
43 {
7c521895 44 conn.disconnect();
5039dede
AK
45
46 if (bVerbose)
47 {
011726a0
VK
48 _tprintf(_T("Agent upgrade started, waiting for completion...\n")
49 _T("[............................................................]\r["));
5039dede
AK
50 fflush(stdout);
51 for(i = 0; i < 120; i += 2)
52 {
53 ThreadSleep(2);
011726a0 54 _puttc(_T('*'), stdout);
5039dede
AK
55 fflush(stdout);
56 if ((i % 20 == 0) && (i > 30))
57 {
7c521895 58 if (conn.connect(pServerKey, FALSE))
5039dede
AK
59 {
60 bConnected = TRUE;
61 break; // Connected successfully
62 }
63 }
64 }
011726a0 65 _puttc(_T('\n'), stdout);
5039dede
AK
66 }
67 else
68 {
69 ThreadSleep(20);
70 for(i = 20; i < 120; i += 20)
71 {
72 ThreadSleep(20);
7c521895 73 if (conn.connect(pServerKey, FALSE))
5039dede
AK
74 {
75 bConnected = TRUE;
76 break; // Connected successfully
77 }
78 }
79 }
80
81 // Last attempt to reconnect
82 if (!bConnected)
7c521895 83 bConnected = conn.connect(pServerKey, FALSE);
5039dede
AK
84
85 if (bConnected && bVerbose)
86 {
011726a0 87 _tprintf(_T("Successfully established connection to agent after upgrade\n"));
5039dede
AK
88 }
89 else
90 {
011726a0 91 _ftprintf(stderr, _T("Failed to establish connection to the agent after upgrade\n"));
5039dede
AK
92 }
93 }
94 else
95 {
96 if (bVerbose)
011726a0 97 _ftprintf(stderr, _T("%d: %s\n"), dwError, AgentErrorCodeToText(dwError));
5039dede
AK
98 }
99
100 return bConnected ? 0 : 1;
101}
102
503da871
VK
103/**
104 * Upload progress callback
105 */
6173bea8
VK
106static void ProgressCallback(INT64 bytesTransferred, void *cbArg)
107{
108#ifdef _WIN32
011726a0 109 _tprintf(_T("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b%-16I64d"), bytesTransferred);
6173bea8 110#else
011726a0 111 _tprintf(_T("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b%-16lld"), bytesTransferred);
6173bea8
VK
112#endif
113}
114
503da871
VK
115/**
116 * Startup
117 */
5039dede
AK
118int main(int argc, char *argv[])
119{
120 char *eptr;
121 BOOL bStart = TRUE, bVerbose = TRUE, bUpgrade = FALSE;
122 int i, ch, iExitCode = 3;
123 int iAuthMethod = AUTH_NONE;
124#ifdef _WITH_ENCRYPTION
125 int iEncryptionPolicy = ENCRYPTION_ALLOWED;
126#else
127 int iEncryptionPolicy = ENCRYPTION_DISABLED;
128#endif
129 WORD wPort = AGENT_LISTEN_PORT;
c11eee9b 130 UINT32 dwTimeout = 5000, dwConnTimeout = 30000, dwError;
5039dede 131 INT64 nElapsedTime;
06a93345
VK
132 TCHAR szSecret[MAX_SECRET_LENGTH] = _T("");
133 TCHAR szKeyFile[MAX_PATH] = DEFAULT_DATA_DIR DFILE_KEYS;
95a7f792 134 TCHAR szDestinationFile[MAX_PATH] = {0};
5039dede 135 RSA *pServerKey = NULL;
503da871 136 NXCPCompressionMethod compression = NXCP_COMPRESSION_NONE;
5039dede
AK
137
138 // Parse command line
139 opterr = 1;
503da871 140 while((ch = getopt(argc, argv, "a:d:e:hK:p:qs:uvw:W:z")) != -1)
5039dede
AK
141 {
142 switch(ch)
143 {
144 case 'h': // Display help and exit
06a93345
VK
145 _tprintf(_T("Usage: nxupload [<options>] <host> <file>\n")
146 _T("Valid options are:\n")
147 _T(" -a <auth> : Authentication method. Valid methods are \"none\",\n")
148 _T(" \"plain\", \"md5\" and \"sha1\". Default is \"none\".\n")
619e5c9b 149 _T(" -d <file> : Fully qualified destination file name\n")
5039dede 150#ifdef _WITH_ENCRYPTION
06a93345
VK
151 _T(" -e <policy> : Set encryption policy. Possible values are:\n")
152 _T(" 0 = Encryption disabled;\n")
153 _T(" 1 = Encrypt connection only if agent requires encryption;\n")
154 _T(" 2 = Encrypt connection if agent supports encryption;\n")
155 _T(" 3 = Force encrypted connection;\n")
156 _T(" Default value is 1.\n")
5039dede 157#endif
06a93345 158 _T(" -h : Display help and exit.\n")
5039dede 159#ifdef _WITH_ENCRYPTION
06a93345
VK
160 _T(" -K <file> : Specify server's key file\n")
161 _T(" (default is ") DEFAULT_DATA_DIR DFILE_KEYS _T(").\n")
5039dede 162#endif
06a93345
VK
163 _T(" -p <port> : Specify agent's port number. Default is %d.\n")
164 _T(" -q : Quiet mode.\n")
165 _T(" -s <secret> : Specify shared secret for authentication.\n")
166 _T(" -u : Start agent upgrade from uploaded package.\n")
167 _T(" -v : Display version and exit.\n")
168 _T(" -w <seconds> : Set command timeout (default is 5 seconds)\n")
169 _T(" -W <seconds> : Set connection timeout (default is 30 seconds)\n")
503da871 170 _T(" -z : Compress data stream.\n")
06a93345 171 _T("\n"), wPort);
5039dede
AK
172 bStart = FALSE;
173 break;
174 case 'a': // Auth method
175 if (!strcmp(optarg, "none"))
176 iAuthMethod = AUTH_NONE;
177 else if (!strcmp(optarg, "plain"))
178 iAuthMethod = AUTH_PLAINTEXT;
179 else if (!strcmp(optarg, "md5"))
180 iAuthMethod = AUTH_MD5_HASH;
181 else if (!strcmp(optarg, "sha1"))
182 iAuthMethod = AUTH_SHA1_HASH;
183 else
184 {
06a93345 185 _tprintf(_T("Invalid authentication method \"%hs\"\n"), optarg);
5039dede
AK
186 bStart = FALSE;
187 }
188 break;
189 case 'p': // Port number
190 i = strtol(optarg, &eptr, 0);
191 if ((*eptr != 0) || (i < 0) || (i > 65535))
192 {
06a93345 193 _tprintf(_T("Invalid port number \"%hs\"\n"), optarg);
5039dede
AK
194 bStart = FALSE;
195 }
196 else
197 {
198 wPort = (WORD)i;
199 }
200 break;
201 case 'q': // Quiet mode
202 bVerbose = FALSE;
203 break;
204 case 's': // Shared secret
06a93345
VK
205#ifdef UNICODE
206 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, optarg, -1, szSecret, MAX_SECRET_LENGTH);
207 szSecret[MAX_SECRET_LENGTH - 1] = 0;
208#else
209 nx_strncpy(szSecret, optarg, MAX_SECRET_LENGTH);
210#endif
5039dede
AK
211 break;
212 case 'u': // Upgrade agent
213 bUpgrade = TRUE;
214 break;
215 case 'v': // Print version and exit
06a93345 216 _tprintf(_T("NetXMS UPLOAD command-line utility Version ") NETXMS_VERSION_STRING _T("\n"));
5039dede
AK
217 bStart = FALSE;
218 break;
219 case 'w': // Command timeout
220 i = strtol(optarg, &eptr, 0);
221 if ((*eptr != 0) || (i < 1) || (i > 120))
222 {
06a93345 223 _tprintf(_T("Invalid timeout \"%hs\"\n"), optarg);
5039dede
AK
224 bStart = FALSE;
225 }
226 else
227 {
967893bb 228 dwTimeout = (UINT32)i * 1000; // Convert to milliseconds
5039dede
AK
229 }
230 break;
7c521895
VK
231 case 'W': // Connection timeout
232 i = strtol(optarg, &eptr, 0);
233 if ((*eptr != 0) || (i < 1) || (i > 120))
234 {
06a93345 235 _tprintf(_T("Invalid timeout \"%hs\"\n"), optarg);
7c521895
VK
236 bStart = FALSE;
237 }
238 else
239 {
967893bb 240 dwConnTimeout = (UINT32)i * 1000; // Convert to milliseconds
7c521895
VK
241 }
242 break;
5039dede
AK
243#ifdef _WITH_ENCRYPTION
244 case 'e':
245 iEncryptionPolicy = atoi(optarg);
246 if ((iEncryptionPolicy < 0) ||
247 (iEncryptionPolicy > 3))
248 {
06a93345 249 _tprintf(_T("Invalid encryption policy %d\n"), iEncryptionPolicy);
5039dede
AK
250 bStart = FALSE;
251 }
252 break;
253 case 'K':
06a93345
VK
254#ifdef UNICODE
255 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, optarg, -1, szKeyFile, MAX_PATH);
256 szKeyFile[MAX_PATH - 1] = 0;
257#else
5039dede 258 nx_strncpy(szKeyFile, optarg, MAX_PATH);
06a93345 259#endif
5039dede
AK
260 break;
261#else
262 case 'e':
263 case 'K':
264 if (bVerbose)
06a93345 265 _tprintf(_T("ERROR: This tool was compiled without encryption support\n"));
5039dede
AK
266 bStart = FALSE;
267 break;
268#endif
f0c1d2a4
AK
269 case 'd':
270#ifdef UNICODE
271 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, optarg, -1, szDestinationFile, MAX_PATH);
272 szDestinationFile[MAX_PATH - 1] = 0;
273#else
274 nx_strncpy(szDestinationFile, optarg, MAX_PATH);
275#endif
276 break;
503da871
VK
277 case 'z':
278 compression = NXCP_COMPRESSION_LZ4;
279 break;
5039dede
AK
280 case '?':
281 bStart = FALSE;
282 break;
283 default:
284 break;
285 }
286 }
287
288 // Check parameter correctness
289 if (bStart)
290 {
291 if (argc - optind < 2)
292 {
293 if (bVerbose)
06a93345 294 _tprintf(_T("Required argument(s) missing.\nUse nxupload -h to get complete command line syntax.\n"));
5039dede
AK
295 bStart = FALSE;
296 }
297 else if ((iAuthMethod != AUTH_NONE) && (szSecret[0] == 0))
298 {
299 if (bVerbose)
06a93345 300 _tprintf(_T("Shared secret not specified or empty\n"));
5039dede
AK
301 bStart = FALSE;
302 }
303
304 // Load server key if requested
305#ifdef _WITH_ENCRYPTION
306 if ((iEncryptionPolicy != ENCRYPTION_DISABLED) && bStart)
307 {
6468147c 308 if (InitCryptoLib(0xFFFF, NULL))
5039dede
AK
309 {
310 pServerKey = LoadRSAKeys(szKeyFile);
311 if (pServerKey == NULL)
312 {
313 if (bVerbose)
06a93345 314 _tprintf(_T("Error loading RSA keys from \"%s\"\n"), szKeyFile);
5039dede
AK
315 if (iEncryptionPolicy == ENCRYPTION_REQUIRED)
316 bStart = FALSE;
317 }
318 }
319 else
320 {
321 if (bVerbose)
06a93345 322 _tprintf(_T("Error initializing cryptografy module\n"));
5039dede
AK
323 if (iEncryptionPolicy == ENCRYPTION_REQUIRED)
324 bStart = FALSE;
325 }
326 }
327#endif
328
329 // If everything is ok, start communications
330 if (bStart)
331 {
5039dede
AK
332 // Initialize WinSock
333#ifdef _WIN32
334 WSADATA wsaData;
335 WSAStartup(2, &wsaData);
336#endif
337
c11eee9b
VK
338 InetAddress addr = InetAddress::resolveHostName(argv[optind]);
339 if (!addr.isValid())
5039dede
AK
340 {
341 if (bVerbose)
06a93345 342 _tprintf(_T("Invalid host name or address specified\n"));
5039dede
AK
343 }
344 else
345 {
c11eee9b 346 AgentConnection conn(addr, wPort, iAuthMethod, szSecret);
5039dede 347
7c521895 348 conn.setConnectionTimeout(dwConnTimeout);
45d84f8a 349 conn.setCommandTimeout(dwTimeout);
7c521895
VK
350 conn.setEncryptionPolicy(iEncryptionPolicy);
351 if (conn.connect(pServerKey, bVerbose, &dwError))
5039dede 352 {
967893bb 353 UINT32 dwError;
5039dede 354
06a93345
VK
355#ifdef UNICODE
356 WCHAR fname[MAX_PATH];
357 MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, argv[optind + 1], -1, fname, MAX_PATH);
358 fname[MAX_PATH - 1] = 0;
359#else
360#define fname argv[optind + 1]
361#endif
5039dede 362 nElapsedTime = GetCurrentTimeMs();
6173bea8 363 if (bVerbose)
06a93345 364 _tprintf(_T("Upload: "));
503da871 365 dwError = conn.uploadFile(fname, szDestinationFile[0] != 0 ? szDestinationFile : NULL, bVerbose ? ProgressCallback : NULL, NULL, compression);
6173bea8 366 if (bVerbose)
06a93345 367 _tprintf(_T("\r \r"));
5039dede
AK
368 nElapsedTime = GetCurrentTimeMs() - nElapsedTime;
369 if (bVerbose)
370 {
371 if (dwError == ERR_SUCCESS)
372 {
373 QWORD qwBytes;
374
06a93345
VK
375 qwBytes = FileSize(fname);
376 _tprintf(_T("File transferred successfully\n") UINT64_FMT _T(" bytes in %d.%03d seconds (%.2f KB/sec)\n"),
377 qwBytes, (LONG)(nElapsedTime / 1000),
378 (LONG)(nElapsedTime % 1000),
379 ((double)((INT64)qwBytes / 1024) / (double)nElapsedTime) * 1000);
5039dede
AK
380 }
381 else
382 {
06a93345 383 _tprintf(_T("%d: %s\n"), dwError, AgentErrorCodeToText(dwError));
5039dede
AK
384 }
385 }
386
387 if (bUpgrade && (dwError == RCC_SUCCESS))
388 {
06a93345 389 iExitCode = UpgradeAgent(conn, fname, bVerbose, pServerKey);
5039dede
AK
390 }
391 else
392 {
393 iExitCode = (dwError == ERR_SUCCESS) ? 0 : 1;
394 }
7c521895 395 conn.disconnect();
5039dede
AK
396 }
397 else
398 {
399 if (bVerbose)
06a93345 400 _tprintf(_T("%d: %s\n"), dwError, AgentErrorCodeToText(dwError));
5039dede
AK
401 iExitCode = 2;
402 }
403 }
404 }
405 }
406
d87ddcc2 407 MsgWaitQueue::shutdown();
5039dede
AK
408 return iExitCode;
409}