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