RDK Documentation (Open Sourced RDK Components)
rmh_monitor.c
1 /*
2  * If not stated otherwise in this file or this component's Licenses.txt file the
3  * following copyright and licenses apply:
4  *
5  * Copyright 2016 RDK Management
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18 */
19 
20 #include <stdarg.h>
21 #include <pthread.h>
22 #include <sys/time.h>
23 #include "rmh_monitor.h"
24 #include <unistd.h>
25 #include <sys/select.h>
26 #include "rfcapi.h"
27 
28 static
29 RMH_Result RMHMonitor_PrintUsage(RMHMonitor *app) {
30  RMH_PrintMsg("usage: rmh_monitor [-?|-h|--help] [-n|--no_timestamp] [-t|--trace]\n");
31  RMH_PrintMsg("\n");
32  RMH_PrintMsg(" --help Print this help message\n");
33  RMH_PrintMsg(" --no_timestamp Do not prefix each line with a timestamp\n");
34  RMH_PrintMsg(" --timestamp Prefix each line with a timestamp\n");
35  RMH_PrintMsg(" --out-file Write log messages to a file\n");
36  RMH_PrintMsg(" --debug Enable API trace messages\n");
37  RMH_PrintMsg("\n");
38  RMH_PrintMsg("\n");
39  return RMH_SUCCESS;
40 }
41 
42 
43 /**
44  * This function handles all incomming callbacks from RMH. If they are print events we will handle them right away.
45  * All other callbacks will be enqueued to be handled by the event thread.
46 */
47 void RMHMonitor_RMHCallback(const enum RMH_Event event, const struct RMH_EventData *eventData, void* userContext){
48  RMHMonitor *app=(RMHMonitor *)userContext;
49 
50  switch(event) {
51  case RMH_EVENT_API_PRINT:
52  RMH_PrintMsg("%s", eventData->RMH_EVENT_API_PRINT.logMsg);
53  break;
54  case RMH_EVENT_DRIVER_PRINT:
55  RMH_PrintMsg("%s", eventData->RMH_EVENT_DRIVER_PRINT.logMsg);
56  break;
57  default:
58  RMHMonitor_Queue_Enqueue(app, event, eventData);
59  break;
60  }
61 }
62 
63 
64 /**
65  * Handle all arguments passed into the application. Arguments should update values in 'app' and then used later.
66 */
67 static
68 RMH_Result RMHMonitor_ParseArguments(RMHMonitor *app, const int argc, const char *argv[]) {
69  uint32_t i=1; /* start at one to skip program name */
70 
71  while (argc && i<argc) {
72  const char* option = argv[i];
73  if (!option) break;
74 
75  if (option[0] == '-') {
76  if (strcmp(option, "-t") == 0 || strcmp(option, "--trace") == 0) {
77  app->apiLogLevel |= RMH_LOG_TRACE;
78  } else if (strcmp(option, "-n") == 0 || strcmp(option, "--no_timestamp") == 0) {
79  app->printTimestamp = false;
80  app->userSetTimestamps = true;
81  } else if (strcmp(option, "-p") == 0 || strcmp(option, "--timestamp") == 0) {
82  app->printTimestamp = true;
83  app->userSetTimestamps = true;
84  } else if (strcmp(option, "-s") == 0 || strcmp(option, "--service_mode") == 0) {
85  app->serviceMode = true;
86  } else if (strcmp(option, "-o") == 0 || strcmp(option, "--out-file") == 0) {
87  app->out_file_name = argv[++i];;
88  } else if (strcmp(option, "-?") == 0 || strcmp(option, "-h") == 0 || strcmp(option, "--help") == 0) {
89  return RMH_FAILURE;
90  } else {
91  RMH_PrintWrn("Unknown option '%s'! Skipping\n", option);
92  return RMH_FAILURE;
93  }
94  }
95  else {
96  RMH_PrintWrn("Unknown argument '%s'! Skipping\n", option);
97  return RMH_FAILURE;
98  }
99  i++;
100  };
101 
102  return RMH_SUCCESS;
103 }
104 
105 
106 /**
107  * Connect to RMH and do logging. Returns true if we should anotehr connect should be attempted
108 */
109 static
110 bool RMHMonitor_Connect(RMHMonitor *app) {
111  /* Connect to RMH */
112  app->rmh=RMH_Initialize(RMHMonitor_RMHCallback, app);
113  if (!app->rmh){
114  RMH_PrintErr("Unable to connect to RMH. Ensure MoCA is running...\n");
115  return true;
116  }
117 
118  /* Setup all the callbacks we're interested in...pretty much everything */
119  if (RMH_SetEventCallbacks(app->rmh, RMH_EVENT_LINK_STATUS_CHANGED | \
120  RMH_EVENT_MOCA_RESET | \
121  RMH_EVENT_MOCA_VERSION_CHANGED | \
122  RMH_EVENT_ADMISSION_STATUS_CHANGED | \
123  RMH_EVENT_NODE_JOINED | \
124  RMH_EVENT_NODE_DROPPED | \
125  RMH_EVENT_NC_ID_CHANGED | \
126  RMH_EVENT_LOW_BANDWIDTH | \
127  RMH_EVENT_API_PRINT) != RMH_SUCCESS) {
128  RMH_PrintErr("Failed setting callback events!\n");
129  return true;
130  }
131 
132  /* Notify what logging level we want */
133  if (RMH_Log_SetAPILevel(app->rmh, app->apiLogLevel ) != RMH_SUCCESS) {
134  RMH_PrintErr("Failed to set the log level!\n");
135  }
136 
137  /* Mark the thread as running and create it */
138  app->eventThreadRunning=true;
139  if (pthread_create(&app->eventThread, NULL, (void *)RMHMonitor_Event_Thread, app) != 0) {
140  RMH_PrintErr("Failed creating event thread!");
141  return false;
142  }
143  else {
144  void *ret;
145  /* Exit the pthread */
146  if(pthread_join(app->eventThread, &ret)) {
147  RMH_PrintErr("Failed doing pthread_join");
148  }
149  app->eventThreadRunning=false;
150  return ret ? true : false;
151  }
152  return true;
153 }
154 
155 static
156 RMH_Result pRMHMonitor_RFC_GetBool(RMHMonitor* app, const char*name, bool *value) {
157  RFC_ParamData_t rfcParam;
158  WDMP_STATUS wdmpStatus;
159 
160  wdmpStatus = getRFCParameter("RMH", name, &rfcParam);
161  if (wdmpStatus != WDMP_SUCCESS) {
162  if (wdmpStatus != WDMP_ERR_VALUE_IS_EMPTY) {
163  RMH_PrintErr("Failed reading from RFC -- %s -- Returned error '%s' (%u)\n", rfcParam.name, getRFCErrorString(wdmpStatus), wdmpStatus);
164  }
165  return RMH_FAILURE;
166  }
167 
168  /* There are cases where RFC type might not be set correctly. To be sure of our value We'll explictly test both values. */
169  if (0 == strcasecmp(rfcParam.value, "TRUE")) {
170  *value=true;
171  }
172  else if (0 == strcasecmp(rfcParam.value, "FALSE")) {
173  *value=false;
174  }
175  else {
176  RMH_PrintErr("Unexpected value reading from RFC -- %s -- Returned '%s' which is type %u. Expecting type WDMP_BOOLEAN(%u)\n", rfcParam.name, rfcParam.value, getRFCErrorString(wdmpStatus), rfcParam.type, WDMP_BOOLEAN);
177  return RMH_FAILURE;
178  }
179 
180  return RMH_SUCCESS;
181 }
182 
183 /***********************************************************
184  * Main
185  ***********************************************************/
186 int main(const int argc, const char *argv[])
187 {
188  RMHMonitor appStr;
189  RMHMonitor* app=&appStr;
190  RMH_Result result=RMH_FAILURE;
191  fd_set fdset;
192  struct timeval timeout;
193  bool rmhEnableStatusLogging = false;
194 
195  /* Initialize everything to defaults */
196  memset(app, 0, sizeof(*app));
197  app->apiLogLevel = RMH_LOG_DEFAULT;
198 
199  if (RMH_SUCCESS == pRMHMonitor_RFC_GetBool(app, "Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.RMHLogging.Enable", &rmhEnableStatusLogging)) {
200  if (rmhEnableStatusLogging == false) {
201  RMH_PrintErr("RMH Loogging has been disabled via RFC. Exiting\n");
202  exit(0);
203  }
204  }
205 
206  /* Try to autodetect if we're running a a service */
207  FD_ZERO(&fdset);
208  FD_SET(fileno(stdin), &fdset);
209  timeout.tv_sec = 0;
210  timeout.tv_usec = 1;
211  if (select(fileno(stdin)+1, &fdset, NULL, NULL, &timeout) == 1) {
212  app->serviceMode = true;
213  }
214 
215  /* Parse arguments and update options in app */
216  if (RMHMonitor_ParseArguments(app, argc, argv) != RMH_SUCCESS) {
217  RMHMonitor_PrintUsage(app);
218  exit(0);
219  }
220 
221  if (!app->userSetTimestamps) {
222  /*If the user didn't set timestamp mode then enable only if we're not in service mode.*/
223  app->printTimestamp = !app->serviceMode;
224  }
225 
226  if (app->out_file_name) {
227  app->out_file=fopen(app->out_file_name, "w");
228  if ( !app->out_file ) {
229  RMH_PrintErr("Failed to open file '%s' for writing. Falling back to stdout\n", app->out_file_name);
230  }
231  }
232 
233  /* Initialize the event queue. The event queue and thread allow us to get out of the callbacks ASAP */
234  TAILQ_INIT(&app->eventQueue);
235  app->eventNotify=RMHMonitor_Semaphore_Create();
236  if (!app->eventNotify) {
237  RMH_PrintErr("Failed RMHMonitor_Semaphore_Create!\n");
238  goto exit_err_init;
239  }
240 
241  /* Setup the event notify semaphore */
242  RMHMonitor_Semaphore_Reset(app->eventNotify);
243 
244  /* Initialize the threads mutex */
245  if (pthread_mutex_init(&app->eventQueueProtect, NULL) != 0) {
246  RMH_PrintErr("Failed to create eventQueueProtect mutex!\n");
247  goto exit_err_eventNotify;
248  }
249 
250  if (!app->serviceMode) {
251  RMH_PrintMsg("Begin monitoring MoCA status. Press Ctrl+C to exit...\n");
252  }
253 
254  while (true) {
255  bool reconnect;
256 
257  app->appPrefix="[ INIT ] ";
258  RMH_PrintMsg("Attempting to connect to MoCA\n");
259 
260  reconnect=RMHMonitor_Connect(app);
261  /* Cleanup*/
262  if (app->rmh) {
263  RMH_Destroy(app->rmh);
264  app->rmh=NULL;
265  }
266 
267  /* If we're not reconnecting, exit the loop */
268  if (!reconnect) break;
269 
270  RMH_PrintErr("There was a failure communicating with the MoCA driver. Sleeping %u seconds before attempting to reconnect...\n", app->reconnectSeconds);
271  /* Sleep 5 seconds before trying again */
272  usleep(app->reconnectSeconds*1000000);
273  app->reconnectSeconds+=5;
274  if (app->reconnectSeconds > 60) app->reconnectSeconds = 60;
275  }
276 
277  RMH_PrintMsg("Exit status monitor\n");
278 
279  if (app->out_file) {
280  fclose(app->out_file);
281  }
282 
283  /************ Exit ************/
284  /* We made it to the end with no filures, indicate as such */
285  result=RMH_SUCCESS;
286 
287  /* Shutdown the event queue */
288  pthread_mutex_destroy (&app->eventQueueProtect);
289 
290 exit_err_eventNotify:
291  /* Delete the semaphore */
292  if (app->eventNotify) {
293  RMHMonitor_Semaphore_Destroy(app->eventNotify);
294  app->eventNotify = NULL;
295  }
296 
297 exit_err_init:
298 
299  return result;
300 }
RMHMonitor
Definition: rmh_monitor.h:77
RMH_EventData
Definition: rmh_type.h:157
RMH_Initialize
RMH_Handle RMH_Initialize(const RMH_EventCallback eventCB, void *userContext)
Initialize the RMH library and return a handle to the instance.
Definition: librmh_api_no_wrap.c:23
RMH_Log_SetAPILevel
RMH_Result RMH_Log_SetAPILevel(const RMH_Handle handle, const uint32_t value)
Set the log level of the RMH library.
RMH_SetEventCallbacks
RMH_Result RMH_SetEventCallbacks(RMH_Handle handle, const uint32_t value)
Set the list of callbacks you wish to receive.
RMH_Destroy
RMH_Result RMH_Destroy(RMH_Handle handle)
Destroy the instance of RMH library which was created by RMH_Initialize.