6926d1b45ba77a8c1b3291f83e13a671a1790ac6
[public/netxms.git] / src / server / core / package.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003, 2004, 2005 Victor Kirhenshtein
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 **
19 ** $module: package.cpp
20 **
21 **/
22
23 #include "nxcore.h"
24
25
26 //
27 // Check if package with specific parameters already installed
28 //
29
30 BOOL IsPackageInstalled(TCHAR *pszName, TCHAR *pszVersion, TCHAR *pszPlatform)
31 {
32 DB_RESULT hResult;
33 TCHAR szQuery[1024], *pszEscName, *pszEscVersion, *pszEscPlatform;
34 BOOL bResult = FALSE;
35
36 pszEscName = EncodeSQLString(pszName);
37 pszEscVersion = EncodeSQLString(pszVersion);
38 pszEscPlatform = EncodeSQLString(pszPlatform);
39 _sntprintf(szQuery, 1024, _T("SELECT pkg_id FROM agent_pkg WHERE "
40 "pkg_name='%s' AND version='%s' AND platform='%s'"),
41 pszEscName, pszEscVersion, pszEscPlatform);
42 free(pszEscName);
43 free(pszEscVersion);
44 free(pszEscPlatform);
45
46 hResult = DBSelect(g_hCoreDB, szQuery);
47 if (hResult != NULL)
48 {
49 bResult = (DBGetNumRows(hResult) > 0);
50 DBFreeResult(hResult);
51 }
52 return bResult;
53 }
54
55
56 //
57 // Check if given package ID is valid
58 //
59
60 BOOL IsValidPackageId(DWORD dwPkgId)
61 {
62 DB_RESULT hResult;
63 TCHAR szQuery[256];
64 BOOL bResult = FALSE;
65
66 _sntprintf(szQuery, 256, _T("SELECT pkg_name FROM agent_pkg WHERE pkg_id=%ld"), dwPkgId);
67 hResult = DBSelect(g_hCoreDB, szQuery);
68 if (hResult != NULL)
69 {
70 bResult = (DBGetNumRows(hResult) > 0);
71 DBFreeResult(hResult);
72 }
73 return bResult;
74 }
75
76
77 //
78 // Check if package file with given name exist
79 //
80
81 BOOL IsPackageFileExist(TCHAR *pszFileName)
82 {
83 TCHAR szFullPath[MAX_PATH];
84
85 _tcscpy(szFullPath, g_szDataDir);
86 _tcscat(szFullPath, DDIR_PACKAGES);
87 _tcscat(szFullPath, FS_PATH_SEPARATOR);
88 _tcscat(szFullPath, pszFileName);
89 return (_taccess(szFullPath, 0) == 0);
90 }
91
92
93 //
94 // Uninstall (remove) package from server
95 //
96
97 DWORD UninstallPackage(DWORD dwPkgId)
98 {
99 TCHAR szQuery[256], szFileName[MAX_PATH];
100 DB_RESULT hResult;
101 DWORD dwResult;
102
103 _sntprintf(szQuery, 256, _T("SELECT pkg_file FROM agent_pkg WHERE pkg_id=%ld"), dwPkgId);
104 hResult = DBSelect(g_hCoreDB, szQuery);
105 if (hResult != NULL)
106 {
107 if (DBGetNumRows(hResult) > 0)
108 {
109 // Delete file from directory
110 _tcscpy(szFileName, g_szDataDir);
111 _tcscat(szFileName, DDIR_PACKAGES);
112 _tcscat(szFileName, FS_PATH_SEPARATOR);
113 _tcscat(szFileName, CHECK_NULL_EX(DBGetField(hResult, 0, 0)));
114 if (_tunlink(szFileName) == 0)
115 {
116 // Delete record from database
117 _sntprintf(szQuery, 256, _T("DELETE FROM agent_pkg WHERE pkg_id=%ld"), dwPkgId);
118 DBQuery(g_hCoreDB, szQuery);
119 dwResult = RCC_SUCCESS;
120 }
121 else
122 {
123 dwResult = RCC_IO_ERROR;
124 }
125 }
126 else
127 {
128 dwResult = RCC_INVALID_PACKAGE_ID;
129 }
130 DBFreeResult(hResult);
131 }
132 else
133 {
134 dwResult = RCC_DB_FAILURE;
135 }
136 return dwResult;
137 }
138
139
140 //
141 // Package deployment worker thread
142 //
143
144 static THREAD_RESULT THREAD_CALL DeploymentThread(void *pArg)
145 {
146 DT_STARTUP_INFO *pStartup = (DT_STARTUP_INFO *)pArg;
147 Node *pNode;
148 CSCPMessage msg;
149 BOOL bSuccess = FALSE;
150 AgentConnection *pAgentConn;
151 char *pszErrorMsg = "";
152
153 // Prepare notification message
154 msg.SetCode(CMD_INSTALLER_INFO);
155 msg.SetId(pStartup->dwRqId);
156
157 while(1)
158 {
159 // Get node object for upgrade
160 pNode = (Node *)pStartup->pQueue->Get();
161 if (pNode == NULL)
162 break; // Queue is empty, exit
163
164 // Preset node id in notification message
165 msg.SetVariable(VID_OBJECT_ID, pNode->Id());
166
167 // Change deployment status to "Initializing"
168 msg.SetVariable(VID_DEPLOYMENT_STATUS, (WORD)DEPLOYMENT_STATUS_INITIALIZE);
169 pStartup->pSession->SendMessage(&msg);
170
171 // Create agent connection
172 pAgentConn = pNode->CreateAgentConnection();
173 if (pAgentConn != NULL)
174 {
175 BOOL bCheckOK = FALSE;
176 char szBuffer[256];
177
178 // Check if package can be deployed on target node
179 if (!stricmp(pStartup->szPlatform, "src"))
180 {
181 // Source package, check if target node
182 // supports source packages
183 if (pAgentConn->GetParameter("Agent.SourcePackageSupport", 32, szBuffer) == ERR_SUCCESS)
184 {
185 bCheckOK = (strtol(szBuffer, NULL, 0) != 0);
186 }
187 }
188 else
189 {
190 // Binary package, check target platform
191 if (pAgentConn->GetParameter("System.PlatformName", 256, szBuffer) == ERR_SUCCESS)
192 {
193 bCheckOK = !stricmp(szBuffer, pStartup->szPlatform);
194 }
195 }
196
197 if (bCheckOK)
198 {
199 // Change deployment status to "File Transfer"
200 msg.SetVariable(VID_DEPLOYMENT_STATUS, (WORD)DEPLOYMENT_STATUS_TRANSFER);
201 pStartup->pSession->SendMessage(&msg);
202
203 // Upload package file to agent
204 strcpy(szBuffer, g_szDataDir);
205 strcat(szBuffer, DDIR_PACKAGES);
206 strcat(szBuffer, FS_PATH_SEPARATOR);
207 strcat(szBuffer, pStartup->szPkgFile);
208 if (pAgentConn->UploadFile(szBuffer) == ERR_SUCCESS)
209 {
210 if (pAgentConn->StartUpgrade(pStartup->szPkgFile) == ERR_SUCCESS)
211 {
212 BOOL bConnected = FALSE;
213 DWORD i;
214
215 // Disconnect from agent
216 pAgentConn->Disconnect();
217
218 // Change deployment status to "Package installation"
219 msg.SetVariable(VID_DEPLOYMENT_STATUS, (WORD)DEPLOYMENT_STATUS_INSTALLATION);
220 pStartup->pSession->SendMessage(&msg);
221
222 // Wait for agent's restart
223 ThreadSleep(20);
224 for(i = 20; i < 120; i += 20)
225 {
226 ThreadSleep(20);
227 if (pAgentConn->Connect())
228 {
229 bConnected = TRUE;
230 break; // Connected successfully
231 }
232 }
233
234 // Last attempt to reconnect
235 if (!bConnected)
236 bConnected = pAgentConn->Connect();
237
238 if (bConnected)
239 {
240 // Check version
241 if (pAgentConn->GetParameter("Agent.Version", MAX_AGENT_VERSION_LEN, szBuffer) == ERR_SUCCESS)
242 {
243 if (!stricmp(szBuffer, pStartup->szVersion))
244 {
245 bSuccess = TRUE;
246 }
247 else
248 {
249 pszErrorMsg = "Agent's version doesn't match package version after upgrade";
250 }
251 }
252 else
253 {
254 pszErrorMsg = "Unable to get agent's version after upgrade";
255 }
256 }
257 else
258 {
259 pszErrorMsg = "Unable to contact agent after upgrade";
260 }
261 }
262 else
263 {
264 pszErrorMsg = "Unable to start upgrade process";
265 }
266 }
267 else
268 {
269 pszErrorMsg = "File transfer failed";
270 }
271 }
272 else
273 {
274 pszErrorMsg = "Package is not compatible with target machine";
275 }
276
277 delete pAgentConn;
278 }
279 else
280 {
281 pszErrorMsg = "Unable to connect to agent";
282 }
283
284 // Finish node processing
285 msg.SetVariable(VID_DEPLOYMENT_STATUS,
286 bSuccess ? (WORD)DEPLOYMENT_STATUS_COMPLETED : (WORD)DEPLOYMENT_STATUS_FAILED);
287 msg.SetVariable(VID_ERROR_MESSAGE, pszErrorMsg);
288 pStartup->pSession->SendMessage(&msg);
289 pNode->DecRefCount();
290 }
291 return THREAD_OK;
292 }
293
294
295 //
296 // Package deployment thread
297 //
298
299 THREAD_RESULT THREAD_CALL DeploymentManager(void *pArg)
300 {
301 DT_STARTUP_INFO *pStartup = (DT_STARTUP_INFO *)pArg;
302 DWORD i, dwNumThreads;
303 CSCPMessage msg;
304 Queue *pQueue;
305 THREAD *pThreadList;
306
307 // Wait for parent initialization completion
308 MutexLock(pStartup->mutex, INFINITE);
309 MutexUnlock(pStartup->mutex);
310
311 // Sanity check
312 if (pStartup->dwNumNodes == 0)
313 return THREAD_OK;
314
315 // Read number of upgrade threads
316 dwNumThreads = ConfigReadInt(_T("NumberOfUpgradeThreads"), 10);
317 if (dwNumThreads > pStartup->dwNumNodes)
318 dwNumThreads = pStartup->dwNumNodes;
319
320 // Create processing queue
321 pQueue = new Queue;
322 pStartup->pQueue = pQueue;
323
324 // Send initial status for each node and queue them for deployment
325 msg.SetCode(CMD_INSTALLER_INFO);
326 msg.SetId(pStartup->dwRqId);
327 for(i = 0; i < pStartup->dwNumNodes; i++)
328 {
329 pQueue->Put(pStartup->ppNodeList[i]);
330 msg.SetVariable(VID_OBJECT_ID, pStartup->ppNodeList[i]->Id());
331 msg.SetVariable(VID_DEPLOYMENT_STATUS, (WORD)DEPLOYMENT_STATUS_PENDING);
332 pStartup->pSession->SendMessage(&msg);
333 msg.DeleteAllVariables();
334 }
335
336 // Start worker threads
337 pThreadList = (THREAD *)malloc(sizeof(THREAD) * dwNumThreads);
338 for(i = 0; i < dwNumThreads; i++)
339 pThreadList[i] = ThreadCreateEx(DeploymentThread, 0, pStartup);
340
341 // Wait for all worker threads termination
342 for(i = 0; i < dwNumThreads; i++)
343 ThreadJoin(pThreadList[i]);
344
345 // Send final notification to client
346 msg.SetVariable(VID_DEPLOYMENT_STATUS, (WORD)DEPLOYMENT_STATUS_FINISHED);
347 pStartup->pSession->SendMessage(&msg);
348
349 // Cleanup
350 MutexDestroy(pStartup->mutex);
351 safe_free(pStartup->ppNodeList);
352 free(pStartup);
353 free(pThreadList);
354 delete pQueue;
355
356 return THREAD_OK;
357 }