- Changes in logging and debug output
[public/netxms.git] / src / server / core / actions.cpp
CommitLineData
7e495d97
VK
1/*
2** NetXMS - Network Management System
3** Copyright (C) 2003, 2004 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: actions.cpp
20**
21**/
22
23#include "nms_core.h"
24
25
26//
27// Static data
28//
29
30static DWORD m_dwNumActions = 0;
4ae6b70e 31static NXC_ACTION *m_pActionList = NULL;
c9363772
VK
32static RWLOCK m_rwlockActionListAccess;
33static DWORD m_dwUpdateCode;
34
35
36//
37// Send updates to all connected clients
38//
39
40static void SendActionDBUpdate(ClientSession *pSession, void *pArg)
41{
42 pSession->OnActionDBUpdate(m_dwUpdateCode, (NXC_ACTION *)pArg);
43}
7e495d97
VK
44
45
46//
47// Destroy action list
48//
49
c9363772 50static void DestroyActionList(void)
7e495d97
VK
51{
52 DWORD i;
53
c9363772 54 RWLockWriteLock(m_rwlockActionListAccess, INFINITE);
7e495d97
VK
55 if (m_pActionList != NULL)
56 {
57 for(i = 0; i < m_dwNumActions; i++)
c9363772 58 safe_free(m_pActionList[i].pszData);
7e495d97
VK
59 free(m_pActionList);
60 m_pActionList = NULL;
61 m_dwNumActions = 0;
62 }
c9363772 63 RWLockUnlock(m_rwlockActionListAccess);
7e495d97
VK
64}
65
66
67//
68// Load actions list from database
69//
70
c9363772 71static BOOL LoadActions(void)
7e495d97
VK
72{
73 DB_RESULT hResult;
74 BOOL bResult = FALSE;
75 DWORD i;
d2a1dd18 76 char *pStr;
7e495d97 77
a97797f8 78 hResult = DBSelect(g_hCoreDB, "SELECT action_id,action_name,action_type,"
c9363772 79 "is_disabled,rcpt_addr,email_subject,action_data "
a97797f8 80 "FROM actions ORDER BY action_id");
7e495d97
VK
81 if (hResult != NULL)
82 {
83 DestroyActionList();
84 m_dwNumActions = (DWORD)DBGetNumRows(hResult);
4ae6b70e 85 m_pActionList = (NXC_ACTION *)malloc(sizeof(NXC_ACTION) * m_dwNumActions);
7e495d97
VK
86 for(i = 0; i < m_dwNumActions; i++)
87 {
88 m_pActionList[i].dwId = DBGetFieldULong(hResult, i, 0);
a97797f8
VK
89 strncpy(m_pActionList[i].szName, DBGetField(hResult, i, 1), MAX_OBJECT_NAME);
90 m_pActionList[i].iType = DBGetFieldLong(hResult, i, 2);
c9363772 91 m_pActionList[i].bIsDisabled = DBGetFieldLong(hResult, i, 3);
a97797f8 92
c9363772 93 pStr = DBGetField(hResult, i, 4);
4ae6b70e
VK
94 strcpy(m_pActionList[i].szRcptAddr, CHECK_NULL(pStr));
95 DecodeSQLString(m_pActionList[i].szRcptAddr);
a97797f8 96
c9363772 97 pStr = DBGetField(hResult, i, 5);
d2a1dd18 98 strcpy(m_pActionList[i].szEmailSubject, CHECK_NULL(pStr));
4ae6b70e 99 DecodeSQLString(m_pActionList[i].szEmailSubject);
a97797f8 100
c9363772 101 m_pActionList[i].pszData = strdup(DBGetField(hResult, i, 6));
4ae6b70e 102 DecodeSQLString(m_pActionList[i].pszData);
7e495d97
VK
103 }
104 DBFreeResult(hResult);
105 bResult = TRUE;
106 }
107 else
108 {
109 WriteLog(MSG_ACTIONS_LOAD_FAILED, EVENTLOG_ERROR_TYPE, NULL);
110 }
111 return bResult;
112}
113
114
c9363772
VK
115//
116// Initialize action-related stuff
117//
118
119BOOL InitActions(void)
120{
121 BOOL bSuccess = FALSE;
122
123 m_rwlockActionListAccess = RWLockCreate();
124 if (m_rwlockActionListAccess != NULL)
125 bSuccess = LoadActions();
126 return bSuccess;
127}
128
129
130//
131// Cleanup action-related stuff
132//
133
134void CleanupActions(void)
135{
136 DestroyActionList();
137 RWLockDestroy(m_rwlockActionListAccess);
138}
139
140
141//
142// Save action record to database
143//
144
145static void SaveActionToDB(NXC_ACTION *pAction)
146{
147 DB_RESULT hResult;
148 BOOL bExist = FALSE;
149 char szQuery[4096];
150
151 // Check if action with given ID already exist in database
152 sprintf(szQuery, "SELECT action_id FROM actions WHERE action_id=%ld", pAction->dwId);
153 hResult = DBSelect(g_hCoreDB, szQuery);
154 if (hResult != NULL)
155 {
156 bExist = (DBGetNumRows(hResult) > 0);
157 DBFreeResult(hResult);
158 }
159
160 // Prepare and execute INSERT or UPDATE query
161 if (bExist)
162 sprintf(szQuery, "UPDATE actions SET action_name='%s',action_type=%d,is_disabled=%d,"
163 "rcpt_addr='%s',email_subject='%s',action_data='%s'"
164 "WHERE action_id=%ld",
165 pAction->szName, pAction->iType, pAction->bIsDisabled,
166 pAction->szRcptAddr, pAction->szEmailSubject,
167 (pAction->pszData == NULL ? "" : pAction->pszData), pAction->dwId);
168 else
169 sprintf(szQuery, "INSERT INTO actions (action_id,action_name,action_type,"
66f421c4 170 "is_disabled,rcpt_addr,email_subject,action_data) VALUES"
c9363772
VK
171 " (%ld,'%s',%d,%d,'%s','%s','%s')",
172 pAction->dwId,pAction->szName, pAction->iType, pAction->bIsDisabled,
66f421c4 173 pAction->szRcptAddr, pAction->szEmailSubject, CHECK_NULL_EX(pAction->pszData));
c9363772
VK
174 DBQuery(g_hCoreDB, szQuery);
175}
176
177
7e495d97
VK
178//
179// Compare action's id for bsearch()
180//
181
182static int CompareId(const void *key, const void *elem)
183{
4ae6b70e
VK
184 return (DWORD)key < ((NXC_ACTION *)elem)->dwId ? -1 :
185 ((DWORD)key > ((NXC_ACTION *)elem)->dwId ? 1 : 0);
7e495d97
VK
186}
187
188
e91cb822
VK
189//
190// Compare action id for qsort()
191//
192
193static int CompareElements(const void *p1, const void *p2)
194{
195 return ((NXC_ACTION *)p1)->dwId < ((NXC_ACTION *)p2)->dwId ? -1 :
196 (((NXC_ACTION *)p1)->dwId > ((NXC_ACTION *)p2)->dwId ? 1 : 0);
197}
198
199
7e495d97
VK
200//
201// Execute action on specific event
202//
203
204BOOL ExecuteAction(DWORD dwActionId, Event *pEvent)
205{
4ae6b70e 206 NXC_ACTION *pAction;
7e495d97
VK
207 BOOL bSuccess = FALSE;
208
c9363772 209 RWLockReadLock(m_rwlockActionListAccess, INFINITE);
4ae6b70e
VK
210 pAction = (NXC_ACTION *)bsearch((void *)dwActionId, m_pActionList,
211 m_dwNumActions, sizeof(NXC_ACTION), CompareId);
7e495d97
VK
212 if (pAction != NULL)
213 {
c9363772 214 if (pAction->bIsDisabled)
4d5a05a0 215 {
b1dd534d 216 DbgPrintf(AF_DEBUG_ACTIONS, "*actions* Action %d (%s) is disabled and will not be executed",
c9363772
VK
217 dwActionId, pAction->szName);
218 bSuccess = TRUE;
219 }
220 else
221 {
222 char *pszExpandedData;
223
66f421c4 224 pszExpandedData = pEvent->ExpandText(CHECK_NULL_EX(pAction->pszData));
c9363772
VK
225 switch(pAction->iType)
226 {
227 case ACTION_EXEC:
b1dd534d 228 DbgPrintf(AF_DEBUG_ACTIONS, "*actions* Executing command \"%s\"", pszExpandedData);
c9363772
VK
229 bSuccess = ExecCommand(pszExpandedData);
230 break;
231 case ACTION_SEND_EMAIL:
b1dd534d 232 DbgPrintf(AF_DEBUG_ACTIONS, "*actions* Sending mail to %s: \"%s\"",
c9363772 233 pAction->szRcptAddr, pszExpandedData);
b86ba1c0
VK
234 PostMail(pAction->szRcptAddr, pAction->szEmailSubject, pszExpandedData);
235 bSuccess = TRUE;
c9363772
VK
236 break;
237 case ACTION_SEND_SMS:
b1dd534d 238 DbgPrintf(AF_DEBUG_ACTIONS, "*actions* Sending SMS to %s: \"%s\"",
c9363772
VK
239 pAction->szRcptAddr, pszExpandedData);
240 break;
241 case ACTION_REMOTE:
b1dd534d 242 DbgPrintf(AF_DEBUG_ACTIONS, "*actions* Executing on \"%s\": \"%s\"",
c9363772
VK
243 pAction->szRcptAddr, pszExpandedData);
244 break;
245 default:
246 break;
247 }
248 free(pszExpandedData);
4d5a05a0 249 }
7e495d97 250 }
c9363772 251 RWLockUnlock(m_rwlockActionListAccess);
7e495d97
VK
252 return bSuccess;
253}
c9363772
VK
254
255
256//
257// Create new action
258//
259
260DWORD CreateNewAction(char *pszName, DWORD *pdwId)
261{
262 DWORD i, dwResult = RCC_SUCCESS;
263
264 RWLockWriteLock(m_rwlockActionListAccess, INFINITE);
265
266 // Check for duplicate name
267 for(i = 0; i < m_dwNumActions; i++)
268 if (!stricmp(m_pActionList[i].szName, pszName))
269 {
270 dwResult = RCC_ALREADY_EXIST;
271 break;
272 }
273
274 // If not exist, create it
275 if (i == m_dwNumActions)
276 {
277 m_dwNumActions++;
278 m_pActionList = (NXC_ACTION *)realloc(m_pActionList, sizeof(NXC_ACTION) * m_dwNumActions);
279 m_pActionList[i].dwId = CreateUniqueId(IDG_ACTION);
280 strncpy(m_pActionList[i].szName, pszName, MAX_OBJECT_NAME);
281 m_pActionList[i].bIsDisabled = TRUE;
282 m_pActionList[i].iType = ACTION_EXEC;
283 m_pActionList[i].szEmailSubject[0] = 0;
284 m_pActionList[i].szRcptAddr[0] = 0;
285 m_pActionList[i].pszData = NULL;
286
e91cb822
VK
287 qsort(m_pActionList, m_dwNumActions, sizeof(NXC_ACTION), CompareElements);
288
c9363772
VK
289 SaveActionToDB(&m_pActionList[i]);
290 m_dwUpdateCode = NX_NOTIFY_ACTION_CREATED;
291 EnumerateClientSessions(SendActionDBUpdate, &m_pActionList[i]);
292 *pdwId = m_pActionList[i].dwId;
293 }
294
295 RWLockUnlock(m_rwlockActionListAccess);
296 return dwResult;
297}
15b8d488
VK
298
299
300//
301// Delete action
302//
303
304DWORD DeleteActionFromDB(DWORD dwActionId)
305{
306 DWORD i, dwResult = RCC_INVALID_ACTION_ID;
307 char szQuery[256];
308
309 RWLockWriteLock(m_rwlockActionListAccess, INFINITE);
310
311 for(i = 0; i < m_dwNumActions; i++)
312 if (m_pActionList[i].dwId == dwActionId)
313 {
314 m_dwNumActions--;
315 memmove(&m_pActionList[i], &m_pActionList[i + 1], sizeof(NXC_ACTION) * (m_dwNumActions - i));
316 sprintf(szQuery, "DELETE FROM actions WHERE action_id=%ld", dwActionId);
317 DBQuery(g_hCoreDB, szQuery);
318
319 m_dwUpdateCode = NX_NOTIFY_ACTION_DELETED;
320 EnumerateClientSessions(SendActionDBUpdate, &m_pActionList[i]);
321
322 dwResult = RCC_SUCCESS;
323 break;
324 }
325
326 RWLockUnlock(m_rwlockActionListAccess);
327 return dwResult;
328}
329
330
331//
332// Modify action record from message
333//
334
335DWORD ModifyActionFromMessage(CSCPMessage *pMsg)
336{
337 DWORD i, dwResult = RCC_INVALID_ACTION_ID;
338 DWORD dwActionId;
339
340 dwActionId = pMsg->GetVariableLong(VID_ACTION_ID);
341 RWLockWriteLock(m_rwlockActionListAccess, INFINITE);
342
343 // Find action with given id
344 for(i = 0; i < m_dwNumActions; i++)
345 if (m_pActionList[i].dwId == dwActionId)
346 {
347 m_pActionList[i].bIsDisabled = pMsg->GetVariableShort(VID_IS_DISABLED);
348 m_pActionList[i].iType = pMsg->GetVariableShort(VID_ACTION_TYPE);
349 safe_free(m_pActionList[i].pszData);
350 m_pActionList[i].pszData = pMsg->GetVariableStr(VID_ACTION_DATA);
351 pMsg->GetVariableStr(VID_EMAIL_SUBJECT, m_pActionList[i].szEmailSubject, MAX_EMAIL_SUBJECT_LEN);
352 pMsg->GetVariableStr(VID_ACTION_NAME, m_pActionList[i].szName, MAX_OBJECT_NAME);
353 pMsg->GetVariableStr(VID_RCPT_ADDR, m_pActionList[i].szRcptAddr, MAX_RCPT_ADDR_LEN);
354
355 SaveActionToDB(&m_pActionList[i]);
356
357 m_dwUpdateCode = NX_NOTIFY_ACTION_MODIFIED;
358 EnumerateClientSessions(SendActionDBUpdate, &m_pActionList[i]);
359
360 dwResult = RCC_SUCCESS;
361 break;
362 }
363
364 RWLockUnlock(m_rwlockActionListAccess);
365 return dwResult;
366}
e91cb822
VK
367
368
369//
370// Fill CSCP message with action's data
371//
372
373void FillActionInfoMessage(CSCPMessage *pMsg, NXC_ACTION *pAction)
374{
375 pMsg->SetVariable(VID_IS_DISABLED, (WORD)pAction->bIsDisabled);
376 pMsg->SetVariable(VID_ACTION_TYPE, (WORD)pAction->iType);
66f421c4 377 pMsg->SetVariable(VID_ACTION_DATA, CHECK_NULL_EX(pAction->pszData));
e91cb822
VK
378 pMsg->SetVariable(VID_EMAIL_SUBJECT, pAction->szEmailSubject);
379 pMsg->SetVariable(VID_ACTION_NAME, pAction->szName);
380 pMsg->SetVariable(VID_RCPT_ADDR, pAction->szRcptAddr);
381}
382
383
384//
385// Send all actions to client
386//
387
388void SendActionsToClient(ClientSession *pSession, DWORD dwRqId)
389{
390 DWORD i;
391 CSCPMessage msg;
392
393 // Prepare message
394 msg.SetCode(CMD_ACTION_DATA);
395 msg.SetId(dwRqId);
396
397 RWLockReadLock(m_rwlockActionListAccess, INFINITE);
398
399 for(i = 0; i < m_dwNumActions; i++)
400 {
401 msg.SetVariable(VID_ACTION_ID, m_pActionList[i].dwId);
402 FillActionInfoMessage(&msg, &m_pActionList[i]);
66f421c4 403 pSession->SendMessage(&msg);
e91cb822
VK
404 msg.DeleteAllVariables();
405 }
406
407 RWLockUnlock(m_rwlockActionListAccess);
408
409 // Send end-of-list flag
410 msg.SetVariable(VID_ACTION_ID, (DWORD)0);
411 pSession->SendMessage(&msg);
412}