GUI to set agent cache options
[public/netxms.git] / src / agent / core / push.cpp
CommitLineData
4cd1e46b
AK
1/*
2** NetXMS multiplatform core agent
3** Copyright (C) 2003-2013 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 usefu,,
11** but ITHOUT 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** File: push.cpp
20**
21**/
22
23#include "nxagentd.h"
24
25/**
42a3be4f
VK
26 * Request ID
27 */
28static QWORD s_requestId = (QWORD)time(NULL) << 32;
29
30/**
4cd1e46b
AK
31 * Push parameter's data
32 */
4e46505f 33bool PushData(const TCHAR *parameter, const TCHAR *value, UINT32 objectId)
4cd1e46b 34{
b368969c 35 NXCPMessage msg;
4cd1e46b
AK
36 bool success = false;
37
38 AgentWriteDebugLog(6, _T("PushData: \"%s\" = \"%s\""), parameter, value);
39
b368969c
VK
40 msg.setCode(CMD_PUSH_DCI_DATA);
41 msg.setField(VID_NAME, parameter);
42 msg.setField(VID_VALUE, value);
43 msg.setField(VID_OBJECT_ID, objectId);
44 msg.setField(VID_REQUEST_ID, s_requestId++);
4cd1e46b
AK
45
46 if (g_dwFlags & AF_SUBAGENT_LOADER)
47 {
48 success = SendMessageToMasterAgent(&msg);
49 }
50 else
51 {
52 MutexLock(g_hSessionListAccess);
53 for(DWORD i = 0; i < g_dwMaxSessions; i++)
54 if (g_pSessionList[i] != NULL)
55 if (g_pSessionList[i]->canAcceptTraps())
56 {
57 g_pSessionList[i]->sendMessage(&msg);
58 success = true;
59 }
60 MutexUnlock(g_hSessionListAccess);
61 }
62 return success;
63}
64
65/**
66 * Process push request
67 */
68static void ProcessPushRequest(HPIPE hPipe)
69{
70 TCHAR buffer[256];
71
72 AgentWriteDebugLog(5, _T("ProcessPushRequest: connection established"));
6be0a20b 73 PipeMessageReceiver receiver(hPipe, 8192, 1048576); // 8K initial, 1M max
4cd1e46b
AK
74 while(true)
75 {
6be0a20b 76 MessageReceiverResult result;
b368969c 77 NXCPMessage *msg = receiver.readMessage(5000, &result);
4cd1e46b
AK
78 if (msg == NULL)
79 break;
b368969c
VK
80 AgentWriteDebugLog(6, _T("ProcessPushRequest: received message %s"), NXCPMessageCodeName(msg->getCode(), buffer));
81 if (msg->getCode() == CMD_PUSH_DCI_DATA)
4cd1e46b 82 {
b368969c
VK
83 UINT32 objectId = msg->getFieldAsUInt32(VID_OBJECT_ID);
84 UINT32 count = msg->getFieldAsUInt32(VID_NUM_ITEMS);
4e46505f 85 UINT32 varId = VID_PUSH_DCI_DATA_BASE;
4cd1e46b
AK
86 for(DWORD i = 0; i < count; i++)
87 {
88 TCHAR name[MAX_PARAM_NAME], value[MAX_RESULT_LENGTH];
b368969c
VK
89 msg->getFieldAsString(varId++, name, MAX_PARAM_NAME);
90 msg->getFieldAsString(varId++, value, MAX_RESULT_LENGTH);
4e46505f 91 PushData(name, value, objectId);
4cd1e46b
AK
92 }
93 }
94 else
95 {
96 }
97 delete msg;
98 }
99 AgentWriteDebugLog(5, _T("ProcessPushRequest: connection closed"));
100}
101
102/**
103 * Connector thread for external push command
104 */
105#ifdef _WIN32
106
107static THREAD_RESULT THREAD_CALL PushConnector(void *arg)
108{
109 SECURITY_ATTRIBUTES sa;
110 PSECURITY_DESCRIPTOR sd = NULL;
111 SID_IDENTIFIER_AUTHORITY sidAuthWorld = SECURITY_WORLD_SID_AUTHORITY;
112 EXPLICIT_ACCESS ea;
113 PSID sidEveryone = NULL;
114 ACL *acl = NULL;
115 TCHAR errorText[1024];
116
117 // Create a well-known SID for the Everyone group.
118 if(!AllocateAndInitializeSid(&sidAuthWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &sidEveryone))
119 {
120 AgentWriteDebugLog(2, _T("PushConnector: AllocateAndInitializeSid failed (%s)"), GetSystemErrorText(GetLastError(), errorText, 1024));
121 goto cleanup;
122 }
123
124 // Initialize an EXPLICIT_ACCESS structure for an ACE.
125 // The ACE will allow either Everyone or given user to access pipe
126 ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));
127 ea.grfAccessPermissions = (FILE_GENERIC_READ | FILE_GENERIC_WRITE) & ~FILE_CREATE_PIPE_INSTANCE;
128 ea.grfAccessMode = SET_ACCESS;
129 ea.grfInheritance = NO_INHERITANCE;
130 const TCHAR *user = g_config->getValue(_T("/agent/PushUser"), _T("*"));
131 if ((user[0] == 0) || !_tcscmp(user, _T("*")))
132 {
133 ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
134 ea.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
135 ea.Trustee.ptstrName = (LPTSTR)sidEveryone;
136 }
137 else
138 {
139 ea.Trustee.TrusteeForm = TRUSTEE_IS_NAME;
140 ea.Trustee.TrusteeType = TRUSTEE_IS_USER;
141 ea.Trustee.ptstrName = (LPTSTR)user;
142 AgentWriteDebugLog(2, _T("PushConnector: will allow connections only for user %s"), user);
143 }
144
145 // Create a new ACL that contains the new ACEs.
146 if (SetEntriesInAcl(1, &ea, NULL, &acl) != ERROR_SUCCESS)
147 {
148 AgentWriteDebugLog(2, _T("PushConnector: SetEntriesInAcl failed (%s)"), GetSystemErrorText(GetLastError(), errorText, 1024));
149 goto cleanup;
150 }
151
152 sd = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
153 if (sd == NULL)
154 {
155 AgentWriteDebugLog(2, _T("PushConnector: LocalAlloc failed (%s)"), GetSystemErrorText(GetLastError(), errorText, 1024));
156 goto cleanup;
157 }
158
159 if (!InitializeSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION))
160 {
161 AgentWriteDebugLog(2, _T("PushConnector: InitializeSecurityDescriptor failed (%s)"), GetSystemErrorText(GetLastError(), errorText, 1024));
162 goto cleanup;
163 }
164
165 // Add the ACL to the security descriptor.
166 if (!SetSecurityDescriptorDacl(sd, TRUE, acl, FALSE))
167 {
168 AgentWriteDebugLog(2, _T("PushConnector: SetSecurityDescriptorDacl failed (%s)"), GetSystemErrorText(GetLastError(), errorText, 1024));
169 goto cleanup;
170 }
171
172 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
173 sa.bInheritHandle = FALSE;
174 sa.lpSecurityDescriptor = sd;
175 HANDLE hPipe = CreateNamedPipe(_T("\\\\.\\pipe\\nxagentd.push"), PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, 1, 8192, 8192, 0, &sa);
176 if (hPipe == INVALID_HANDLE_VALUE)
177 {
178 AgentWriteDebugLog(2, _T("PushConnector: CreateNamedPipe failed (%s)"), GetSystemErrorText(GetLastError(), errorText, 1024));
179 goto cleanup;
180 }
181
182 AgentWriteDebugLog(2, _T("PushConnector: named pipe created, waiting for connection"));
183 int connectErrors = 0;
184 while(!(g_dwFlags & AF_SHUTDOWN))
185 {
186 BOOL connected = ConnectNamedPipe(hPipe, NULL);
187 if (connected || (GetLastError() == ERROR_PIPE_CONNECTED))
188 {
189 ProcessPushRequest(hPipe);
190 DisconnectNamedPipe(hPipe);
191 connectErrors = 0;
192 }
193 else
194 {
195 AgentWriteDebugLog(2, _T("PushConnector: ConnectNamedPipe failed (%s)"), GetSystemErrorText(GetLastError(), errorText, 1024));
196 connectErrors++;
197 if (connectErrors > 10)
198 break; // Stop this connector if ConnectNamedPipe fails instantly
199 }
200 }
201
202cleanup:
203 if (hPipe != NULL)
204 CloseHandle(hPipe);
205
206 if (sd != NULL)
207 LocalFree(sd);
208
209 if (acl != NULL)
210 LocalFree(acl);
211
212 if (sidEveryone != NULL)
213 FreeSid(sidEveryone);
214
215 AgentWriteDebugLog(2, _T("PushConnector: listener thread stopped"));
216 return THREAD_OK;
217}
218
219#else
220
221static THREAD_RESULT THREAD_CALL PushConnector(void *arg)
222{
223 mode_t prevMask = 0;
224
225 SOCKET hPipe = socket(AF_UNIX, SOCK_STREAM, 0);
226 if (hPipe == INVALID_SOCKET)
227 {
228 AgentWriteDebugLog(2, _T("PushConnector: socket failed (%s)"), _tcserror(errno));
229 goto cleanup;
230 }
231
232 struct sockaddr_un addrLocal;
233 addrLocal.sun_family = AF_UNIX;
234 strcpy(addrLocal.sun_path, "/tmp/.nxagentd.push");
235 unlink(addrLocal.sun_path);
236 prevMask = umask(S_IWGRP | S_IWOTH);
237 if (bind(hPipe, (struct sockaddr *)&addrLocal, SUN_LEN(&addrLocal)) == -1)
238 {
239 AgentWriteDebugLog(2, _T("PushConnector: bind failed (%s)"), _tcserror(errno));
240 umask(prevMask);
241 goto cleanup;
242 }
243 umask(prevMask);
244
245 if (listen(hPipe, 5) == -1)
246 {
247 AgentWriteDebugLog(2, _T("PushConnector: listen failed (%s)"), _tcserror(errno));
248 goto cleanup;
249 }
250
251 while(!(g_dwFlags & AF_SHUTDOWN))
252 {
253 struct sockaddr_un addrRemote;
254 socklen_t size = sizeof(struct sockaddr_un);
255 SOCKET cs = accept(hPipe, (struct sockaddr *)&addrRemote, &size);
256 if (cs > 0)
257 {
258 ProcessPushRequest(cs);
259 shutdown(cs, 2);
260 close(cs);
261 }
262 else
263 {
264 AgentWriteDebugLog(2, _T("PushConnector: accept failed (%s)"), _tcserror(errno));
265 }
266 }
267
268cleanup:
269 if (hPipe != -1)
270 close(hPipe);
271
272 AgentWriteDebugLog(2, _T("PushConnector: listener thread stopped"));
273 return THREAD_OK;
274}
275
276#endif
277
278/**
279 * Start push connector
280 */
281
282void StartPushConnector()
283{
284 ThreadCreate(PushConnector, 0, NULL);
285}