1 /** @file
2
3 A brief file description
4
5 @section license License
6
7 Licensed to the Apache Software Foundation (ASF) under one
8 or more contributor license agreements. See the NOTICE file
9 distributed with this work for additional information
10 regarding copyright ownership. The ASF licenses this file
11 to you under the Apache License, Version 2.0 (the
12 "License"); you may not use this file except in compliance
13 with the License. You may obtain a copy of the License at
14
15 http://www.apache.org/licenses/LICENSE-2.0
16
17 Unless required by applicable law or agreed to in writing, software
18 distributed under the License is distributed on an "AS IS" BASIS,
19 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 See the License for the specific language governing permissions and
21 limitations under the License.
22 */
23
24 /*****************************************************************************
25 * Filename: EventControlMain.cc
26 * Purpose: Handles all event requests from the user.
27 * Created: 01/08/01
28 * Created by: lant
29 *
30 ***************************************************************************/
31
32 #include "tscore/ink_platform.h"
33 #include "tscore/ink_sock.h"
34 #include "LocalManager.h"
35 #include "MgmtSocket.h"
36 #include "MgmtMarshall.h"
37 #include "MgmtUtils.h"
38 #include "EventControlMain.h"
39 #include "CoreAPI.h"
40 #include "NetworkUtilsLocal.h"
41 #include "NetworkMessage.h"
42
43 // variables that are very important
44 ink_mutex mgmt_events_lock;
45 LLQ *mgmt_events;
46 std::unordered_map<int, EventClientT *> accepted_clients; // list of all accepted client connections
47
48 static TSMgmtError handle_event_message(EventClientT *client, void *req, size_t reqlen);
49
50 /*********************************************************************
51 * new_event_client
52 *
53 * purpose: creates a new EventClientT and return pointer to it
54 * input: None
55 * output: EventClientT
56 * note: None
57 *********************************************************************/
58 EventClientT *
new_event_client()59 new_event_client()
60 {
61 EventClientT *ele = static_cast<EventClientT *>(ats_malloc(sizeof(EventClientT)));
62
63 // now set the alarms registered section
64 for (bool &i : ele->events_registered) {
65 i = false;
66 }
67
68 ele->adr = static_cast<struct sockaddr *>(ats_malloc(sizeof(struct sockaddr)));
69 return ele;
70 }
71
72 /*********************************************************************
73 * delete_event_client
74 *
75 * purpose: frees memory allocated for an EventClientT
76 * input: EventClientT
77 * output: None
78 * note: None
79 *********************************************************************/
80 void
delete_event_client(EventClientT * client)81 delete_event_client(EventClientT *client)
82 {
83 if (client) {
84 ats_free(client->adr);
85 ats_free(client);
86 }
87 return;
88 }
89
90 /*********************************************************************
91 * remove_event_client
92 *
93 * purpose: removes the EventClientT from the specified hashtable; includes
94 * removing the binding and freeing the ClientT
95 * input: client - the ClientT to remove
96 * output:
97 *********************************************************************/
98 void
remove_event_client(EventClientT * client,std::unordered_map<int,EventClientT * > & table)99 remove_event_client(EventClientT *client, std::unordered_map<int, EventClientT *> &table)
100 {
101 // close client socket
102 close_socket(client->fd);
103
104 // remove client binding from hash table
105 table.erase(client->fd);
106
107 // free ClientT
108 delete_event_client(client);
109
110 return;
111 }
112
113 /*********************************************************************
114 * init_mgmt_events
115 *
116 * purpose: initializes the mgmt_events queue which is intended to hold
117 * TM events.
118 * input:
119 * output: TS_ERR_xx
120 * note: None
121 *********************************************************************/
122 TSMgmtError
init_mgmt_events()123 init_mgmt_events()
124 {
125 ink_mutex_init(&mgmt_events_lock);
126
127 // initialize queue
128 mgmt_events = create_queue();
129 if (!mgmt_events) {
130 ink_mutex_destroy(&mgmt_events_lock);
131 return TS_ERR_SYS_CALL;
132 }
133
134 return TS_ERR_OKAY;
135 }
136
137 /*********************************************************************
138 * delete_mgmt_events
139 *
140 * purpose: frees the mgmt_events queue.
141 * input:
142 * output: None
143 * note: None
144 *********************************************************************/
145 void
delete_mgmt_events()146 delete_mgmt_events()
147 {
148 // obtain lock
149 ink_mutex_acquire(&mgmt_events_lock);
150
151 // delete the queue associated with the queue of events
152 delete_event_queue(mgmt_events);
153
154 // release it
155 ink_mutex_release(&mgmt_events_lock);
156
157 // kill lock
158 ink_mutex_destroy(&mgmt_events_lock);
159
160 delete_queue(mgmt_events);
161
162 return;
163 }
164
165 /*********************************************************************
166 * delete_event_queue
167 *
168 * purpose: frees queue where the elements are of type TSMgmtEvent* 's
169 * input: LLQ * q - a queue with entries of TSMgmtEvent*'s
170 * output: None
171 * note: None
172 *********************************************************************/
173 void
delete_event_queue(LLQ * q)174 delete_event_queue(LLQ *q)
175 {
176 if (!q) {
177 return;
178 }
179
180 while (!queue_is_empty(q)) {
181 TSMgmtEvent *ele = static_cast<TSMgmtEvent *>(dequeue(q));
182 ats_free(ele);
183 }
184
185 delete_queue(q);
186 return;
187 }
188
189 /*********************************************************************
190 * apiEventCallback
191 *
192 * purpose: callback function registered with alarm processor so that
193 * each time alarm is signalled, can enqueue it in the mgmt_events
194 * queue
195 * input:
196 * output: None
197 * note: None
198 *********************************************************************/
199 void
apiEventCallback(alarm_t newAlarm,const char *,const char * desc)200 apiEventCallback(alarm_t newAlarm, const char * /* ip ATS_UNUSED */, const char *desc)
201 {
202 // create an TSMgmtEvent
203 // addEvent(new_alarm, ip, desc) // adds event to mgmt_events
204 TSMgmtEvent *newEvent;
205
206 newEvent = TSEventCreate();
207 newEvent->id = newAlarm;
208 newEvent->name = get_event_name(newEvent->id);
209 // newEvent->ip = ats_strdup(ip);
210 if (desc) {
211 newEvent->description = ats_strdup(desc);
212 } else {
213 newEvent->description = ats_strdup("None");
214 }
215
216 // add it to the mgmt_events list
217 ink_mutex_acquire(&mgmt_events_lock);
218 enqueue(mgmt_events, newEvent);
219 ink_mutex_release(&mgmt_events_lock);
220
221 return;
222 }
223
224 /*********************************************************************
225 * event_callback_main
226 *
227 * This function is run as a thread in WebIntrMain.cc that listens on a
228 * specified socket. It loops until Traffic Manager dies.
229 * In the loop, it just listens on a socket, ready to accept any connections,
230 * until receives a request from the remote API client. Parse the request
231 * to determine which CoreAPI call to make.
232 *********************************************************************/
233 void *
event_callback_main(void * arg)234 event_callback_main(void *arg)
235 {
236 int ret;
237 int *socket_fd;
238 int con_socket_fd; // main socket for listening to new connections
239
240 socket_fd = static_cast<int *>(arg);
241 con_socket_fd = *socket_fd; // the socket for event callbacks
242
243 Debug("event", "[event_callback_main] listen on socket = %d", con_socket_fd);
244
245 // initialize queue for holding mgmt events
246 if (init_mgmt_events() != TS_ERR_OKAY) {
247 return nullptr;
248 }
249 // register callback with alarms processor
250 lmgmt->alarm_keeper->registerCallback(apiEventCallback);
251
252 // now we can start listening, accepting connections and servicing requests
253 int new_con_fd; // new connection fd when socket accepts connection
254
255 fd_set selectFDs; // for select call
256 EventClientT *client_entry; // an entry of fd to alarms mapping
257 struct timeval timeout;
258
259 while (true) {
260 // LINUX fix: to prevent hard-spin reset timeout on each loop
261 timeout.tv_sec = 1;
262 timeout.tv_usec = 0;
263
264 FD_ZERO(&selectFDs);
265
266 if (con_socket_fd >= 0) {
267 FD_SET(con_socket_fd, &selectFDs);
268 Debug("event", "[event_callback_main] add fd %d to select set", con_socket_fd);
269 }
270 // see if there are more fd to set
271 for (auto &&it : accepted_clients) {
272 client_entry = it.second;
273 if (client_entry->fd >= 0) { // add fd to select set
274 FD_SET(client_entry->fd, &selectFDs);
275 }
276 }
277
278 // select call - timeout is set so we can check events at regular intervals
279 int fds_ready = mgmt_select(FD_SETSIZE, &selectFDs, (fd_set *)nullptr, (fd_set *)nullptr, &timeout);
280
281 // check return
282 if (fds_ready > 0) {
283 // we got connections or requests!
284
285 // first check for connections!
286 if (con_socket_fd >= 0 && FD_ISSET(con_socket_fd, &selectFDs)) {
287 fds_ready--;
288
289 // create a new instance of the fd to alarms registered mapping
290 EventClientT *new_client_con = new_event_client();
291
292 if (!new_client_con) {
293 // Debug ("TS_Control_Main", "can't create new EventClientT for new connection");
294 } else {
295 // accept connection
296 socklen_t addr_len = (sizeof(struct sockaddr));
297 new_con_fd = mgmt_accept(con_socket_fd, new_client_con->adr, &addr_len);
298 new_client_con->fd = new_con_fd;
299 accepted_clients.emplace(new_client_con->fd, new_client_con);
300 Debug("event", "[event_callback_main] Accept new connection: fd=%d", new_con_fd);
301 }
302 } // end if (new_con_fd >= 0 && FD_ISSET(new_con_fd, &selectFDs))
303
304 // some other file descriptor; for each one, service request
305 if (fds_ready > 0) { // RECEIVED A REQUEST from remote API client
306 // see if there are more fd to set - iterate through all entries in hash table
307 for (auto it = accepted_clients.begin(); it != accepted_clients.end();) {
308 client_entry = it->second;
309 ++it; // prevent the breaking of remove_event_client
310 // got information check
311 if (client_entry->fd && FD_ISSET(client_entry->fd, &selectFDs)) {
312 // SERVICE REQUEST - read the op and message into a buffer
313 // clear the fields first
314 void *req;
315 size_t reqlen;
316
317 ret = preprocess_msg(client_entry->fd, &req, &reqlen);
318 if (ret == TS_ERR_NET_READ || ret == TS_ERR_NET_EOF) { // preprocess_msg FAILED!
319 Debug("event", "[event_callback_main] preprocess_msg FAILED; skip!");
320 remove_event_client(client_entry, accepted_clients);
321 continue;
322 }
323
324 ret = handle_event_message(client_entry, req, reqlen);
325 ats_free(req);
326
327 if (ret == TS_ERR_NET_WRITE || ret == TS_ERR_NET_EOF) {
328 Debug("event", "[event_callback_main] ERROR: handle_control_message");
329 remove_event_client(client_entry, accepted_clients);
330 continue;
331 }
332
333 } // end if(client_entry->fd && FD_ISSET(client_entry->fd, &selectFDs))
334 } // end for (auto it = accepted_clients.begin(); it != accepted_clients.end();)
335 } // end if (fds_ready > 0)
336
337 } // end if (fds_ready > 0)
338
339 // ------------ service loop is done, check for events now -------------
340 // for each event in the mgmt_events list, uses the event id to check the
341 // events_registered queue for each client connection to see if that client
342 // has a callback registered for that event_id
343
344 TSMgmtEvent *event;
345
346 if (!mgmt_events || queue_is_empty(mgmt_events)) { // no events to process
347 // fprintf(stderr, "[event_callback_main] NO EVENTS TO PROCESS\n");
348 Debug("event", "[event_callback_main] NO EVENTS TO PROCESS");
349 continue;
350 }
351 // iterate through each event in mgmt_events
352 while (!queue_is_empty(mgmt_events)) {
353 ink_mutex_acquire(&mgmt_events_lock); // acquire lock
354 event = static_cast<TSMgmtEvent *>(dequeue(mgmt_events)); // get what we want
355 ink_mutex_release(&mgmt_events_lock); // release lock
356
357 if (!event) {
358 continue;
359 }
360
361 // fprintf(stderr, "[event_callback_main] have an EVENT TO PROCESS\n");
362
363 // iterate through all entries in hash table, if any
364 for (auto &&it : accepted_clients) {
365 client_entry = it.second;
366 if (client_entry->events_registered[event->id]) {
367 OpType optype = OpType::EVENT_NOTIFY;
368 MgmtMarshallString name = event->name;
369 MgmtMarshallString desc = event->description;
370
371 ret = send_mgmt_request(client_entry->fd, OpType::EVENT_NOTIFY, &optype, &name, &desc);
372 if (ret != TS_ERR_OKAY) {
373 Debug("event", "sending even notification to fd [%d] failed.", client_entry->fd);
374 }
375 }
376 // get next client connection, if any
377 } // end while(con_entry)
378
379 // now we can delete the event
380 // fprintf(stderr, "[event_callback_main] DELETE EVENT\n");
381 TSEventDestroy(event);
382 } // end while (!queue_is_empty)
383
384 } // end while (1)
385
386 // delete tables
387 delete_mgmt_events();
388
389 // iterate through hash table; close client socket connections and remove entry
390 for (auto &&it : accepted_clients) {
391 client_entry = it.second;
392 if (client_entry->fd >= 0) {
393 close_socket(client_entry->fd);
394 }
395 accepted_clients.erase(client_entry->fd); // remove binding
396 delete_event_client(client_entry); // free ClientT
397 }
398 // all entries should be removed and freed already
399 accepted_clients.clear();
400
401 ink_thread_exit(nullptr);
402 return nullptr;
403 }
404
405 /*-------------------------------------------------------------------------
406 HANDLER FUNCTIONS
407 --------------------------------------------------------------------------*/
408
409 /**************************************************************************
410 * handle_event_reg_callback
411 *
412 * purpose: handles request to register a callback for a specific event (or all events)
413 * input: client - the client currently reading the msg from
414 * req - the event_name
415 * output: TS_ERR_xx
416 * note: the req should be the event name; does not send a reply to client
417 *************************************************************************/
418 static TSMgmtError
handle_event_reg_callback(EventClientT * client,void * req,size_t reqlen)419 handle_event_reg_callback(EventClientT *client, void *req, size_t reqlen)
420 {
421 MgmtMarshallInt optype;
422 MgmtMarshallString name = nullptr;
423 TSMgmtError ret;
424
425 ret = recv_mgmt_request(req, reqlen, OpType::EVENT_REG_CALLBACK, &optype, &name);
426 if (ret != TS_ERR_OKAY) {
427 goto done;
428 }
429
430 // mark the specified alarm as "wanting to be notified" in the client's alarm_registered list
431 if (strlen(name) == 0) { // mark all alarms
432 for (bool &i : client->events_registered) {
433 i = true;
434 }
435 } else {
436 int id = get_event_id(name);
437 if (id < 0) {
438 ret = TS_ERR_FAIL;
439 goto done;
440 }
441
442 client->events_registered[id] = true;
443 }
444
445 ret = TS_ERR_OKAY;
446
447 done:
448 ats_free(name);
449 return ret;
450 }
451
452 /**************************************************************************
453 * handle_event_unreg_callback
454 *
455 * purpose: handles request to unregister a callback for a specific event (or all events)
456 * input: client - the client currently reading the msg from
457 * req - the event_name
458 * output: TS_ERR_xx
459 * note: the req should be the event name; does not send reply to client
460 *************************************************************************/
461 static TSMgmtError
handle_event_unreg_callback(EventClientT * client,void * req,size_t reqlen)462 handle_event_unreg_callback(EventClientT *client, void *req, size_t reqlen)
463 {
464 MgmtMarshallInt optype;
465 MgmtMarshallString name = nullptr;
466 TSMgmtError ret;
467
468 ret = recv_mgmt_request(req, reqlen, OpType::EVENT_UNREG_CALLBACK, &optype, &name);
469 if (ret != TS_ERR_OKAY) {
470 goto done;
471 }
472
473 // mark the specified alarm as "wanting to be notified" in the client's alarm_registered list
474 if (strlen(name) == 0) { // mark all alarms
475 for (bool &i : client->events_registered) {
476 i = false;
477 }
478 } else {
479 int id = get_event_id(name);
480 if (id < 0) {
481 ret = TS_ERR_FAIL;
482 goto done;
483 }
484
485 client->events_registered[id] = false;
486 }
487
488 ret = TS_ERR_OKAY;
489
490 done:
491 ats_free(name);
492 return ret;
493 }
494
495 using event_message_handler = TSMgmtError (*)(EventClientT *, void *, size_t);
496
497 static const event_message_handler handlers[] = {
498 nullptr, // RECORD_SET
499 nullptr, // RECORD_GET
500 nullptr, // PROXY_STATE_GET
501 nullptr, // PROXY_STATE_SET
502 nullptr, // RECONFIGURE
503 nullptr, // RESTART
504 nullptr, // BOUNCE
505 nullptr, // EVENT_RESOLVE
506 nullptr, // EVENT_GET_MLT
507 nullptr, // EVENT_ACTIVE
508 handle_event_reg_callback, // EVENT_REG_CALLBACK
509 handle_event_unreg_callback, // EVENT_UNREG_CALLBACK
510 nullptr, // EVENT_NOTIFY
511 nullptr, // DIAGS
512 nullptr, // STATS_RESET_NODE
513 nullptr, // STORAGE_DEVICE_CMD_OFFLINE
514 nullptr, // RECORD_MATCH_GET
515 nullptr, // LIFECYCLE_MESSAGE
516 nullptr, // HOST_STATUS_UP
517 nullptr, // HOST_STATUS_DOWN
518 };
519
520 static TSMgmtError
handle_event_message(EventClientT * client,void * req,size_t reqlen)521 handle_event_message(EventClientT *client, void *req, size_t reqlen)
522 {
523 OpType optype = extract_mgmt_request_optype(req, reqlen);
524
525 if (static_cast<unsigned>(optype) >= countof(handlers)) {
526 goto fail;
527 }
528
529 if (handlers[static_cast<unsigned>(optype)] == nullptr) {
530 goto fail;
531 }
532
533 if (mgmt_has_peereid()) {
534 uid_t euid = -1;
535 gid_t egid = -1;
536
537 // For now, all event messages require privilege. This is compatible with earlier
538 // versions of Traffic Server that always required privilege.
539 if (mgmt_get_peereid(client->fd, &euid, &egid) == -1 || (euid != 0 && euid != geteuid())) {
540 return TS_ERR_PERMISSION_DENIED;
541 }
542 }
543
544 return handlers[static_cast<unsigned>(optype)](client, req, reqlen);
545
546 fail:
547 mgmt_elog(0, "%s: missing handler for type %d event message\n", __func__, static_cast<int>(optype));
548 return TS_ERR_PARAMS;
549 }
550