EMANE  1.2.1
main.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2014,2016 - Adjacent Link LLC, Bridgewater, New Jersey
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * * Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  * * Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in
13  * the documentation and/or other materials provided with the
14  * distribution.
15  * * Neither the name of Adjacent Link LLC nor the names of its
16  * contributors may be used to endorse or promote products derived
17  * from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include "main.h"
36 #include "emane/exception.h"
38 
39 #include <iostream>
40 #include <fstream>
41 #include <mutex>
42 
43 #include <getopt.h>
44 #include <unistd.h>
45 #include <uuid.h>
46 #include <signal.h>
47 
48 namespace
49 {
50  const int DEFAULT_LOG_LEVEL{2};
51 
52  const int DEFAULT_PRIORITY_LEVEL{50};
53 
54  void usage(const std::string & sAppName,std::vector<std::string> additionalOptions)
55  {
56  std::cout<<"usage: "<<sAppName <<" [OPTIONS]... CONFIG_URL"<<std::endl;
57  std::cout<<std::endl;
58  std::cout<<" CONFIG_URL URL of XML configuration file."<<std::endl;
59  std::cout<<std::endl;
60  std::cout<<"options:"<<std::endl;
61  std::cout<<" -d, --daemonize Run in the background."<<std::endl;
62  std::cout<<" -h, --help Print this message and exit."<<std::endl;
63  std::cout<<" -f, --logfile FILE Log to a file instead of stdout."<<std::endl;
64  std::cout<<" -l, --loglevel [0,4] Set initial log level."<<std::endl;
65  std::cout<<" default: "<<DEFAULT_LOG_LEVEL<<std::endl;
66  std::cout<<" --pidfile FILE Write application pid to file."<<std::endl;
67  std::cout<<" -p, --priority [0,99] Set realtime priority level."<<std::endl;
68  std::cout<<" Only used with -r, --realtime."<<std::endl;
69  std::cout<<" default: "<<DEFAULT_PRIORITY_LEVEL<<std::endl;
70  std::cout<<" -r, --realtime Set realtime scheduling."<<std::endl;
71  std::cout<<" --syslog Log to syslog instead of stdout."<<std::endl;
72  std::cout<<" --uuidfile FILE Write the application instance UUID to file."<<std::endl;
73  std::cout<<" -v, --version Print version and exit."<<std::endl;
74  std::cout<<std::endl;
75 
76  if(!additionalOptions.empty())
77  {
78  std::cout<<"additional options:"<<std::endl;
79 
80  for(const auto & option : additionalOptions)
81  {
82  std::cerr<<option<<std::endl;
83  }
84  std::cout<<std::endl;
85  }
86  }
87 
88  std::mutex mutex{};
89 
90  void sighandler(int)
91  {
92  mutex.unlock();
93  }
94 }
95 
96 EMANE::Application::Main::Main(const std::string & sName):
97  sName_{sName}
98 {
99  uuid_generate(uuid_);
100 
102 }
103 
105 {
107 }
108 
109 
110 const uuid_t & EMANE::Application::Main::getUUID() const
111 {
112  return uuid_;
113 }
114 
115 int EMANE::Application::Main::main(int argc, char * argv[])
116 {
118 
119  try
120  {
121  std::vector<option> options =
122  {
123  {"help", 0, nullptr, 'h'},
124  {"realtime", 0, nullptr, 'r'},
125  {"loglevel", 1, nullptr, 'l'},
126  {"logfile", 1, nullptr, 'f'},
127  {"daemonize",0, nullptr, 'd'},
128  {"syslog", 0, nullptr, 1},
129  {"version" , 0, nullptr, 'v'},
130  {"pidfile" , 1, nullptr, 2},
131  {"uuidfile", 1, nullptr, 3},
132  {"priority", 1, nullptr, 'p'},
133  };
134 
135  std::string sOptString{"hrvdf:l:p:"};
136 
137  int iOption{};
138  int iOptionIndex{};
139  bool bDaemonize{};
140  bool bRealtime{};
141  bool bSysLog{};
142  int iLogLevel{DEFAULT_LOG_LEVEL};
143  int iPriority{DEFAULT_PRIORITY_LEVEL};
144  std::string sLogFile{};
145  std::string sPIDFile{};
146  std::string sUUIDFile{};
147 
148  // add any specialized options
149  std::vector<option> additionalOptions = doGetOptions();
150 
151  options.insert(options.end(),
152  additionalOptions.begin(),
153  additionalOptions.end());
154 
155  // add any specialized optsting items
156  sOptString += doGetOptString();
157 
158  // close out the options
159  options.push_back({0, 0,nullptr,0 });
160 
161  while((iOption = getopt_long(argc,argv,sOptString.c_str(), &options[0],&iOptionIndex)) != -1)
162  {
163  switch(iOption)
164  {
165  case 'h':
166  usage(sName_,doGetOptionsUsage());
167  return EXIT_SUCCESS;
168  break;
169 
170  case 'r':
171  bRealtime = true;
172  if(optarg)
173  {
174  std::cout<<optarg<<std::endl;
175  }
176  break;
177 
178  case 1:
179  bSysLog = true;
180  break;
181 
182  case 'd':
183  bDaemonize = true;
184  break;
185 
186  case 'v':
187  std::cout<<VERSION<<std::endl;
188  return EXIT_SUCCESS;
189 
190  case 'f':
191  sLogFile = optarg;
192  break;
193 
194  case 2:
195  sPIDFile = optarg;
196  break;
197 
198  case 3:
199  sUUIDFile = optarg;
200  break;
201 
202  case 'p':
203  try
204  {
205  iPriority = EMANE::Utils::ParameterConvert{optarg}.toINT32(0,99);
206  }
207  catch(...)
208  {
209  std::cerr<<"invalid priority: "<<optarg<<std::endl;
210  return EXIT_FAILURE;
211  }
212 
213  break;
214 
215  case 'l':
216  try
217  {
218  iLogLevel = EMANE::Utils::ParameterConvert{optarg}.toINT32(0,4);
219  }
220  catch(...)
221  {
222  std::cerr<<"invalid log level: "<<optarg<<std::endl;
223  return EXIT_FAILURE;
224  }
225 
226  break;
227 
228 
229  case '?':
230  if(optopt == 't')
231  {
232  std::cerr<<"option -"<<static_cast<char>(optopt)<<" requires an argument."<<std::endl;
233  }
234  else if(isprint(optopt))
235  {
236  std::cerr<<"unknown option -"<<static_cast<char>(optopt)<<"."<<std::endl;
237  }
238  else
239  {
240  std::cerr<<"bad option"<<std::endl;
241  }
242  return EXIT_FAILURE;
243 
244  default:
245  if(!doProcessOption(iOption,optarg))
246  {
247  return EXIT_FAILURE;
248  }
249  }
250  }
251 
252  std::string sConfigurationXML{};
253 
254  if(optind >= argc)
255  {
256  std::cerr<<"Missing CONFIG_URL"<<std::endl;
257  return EXIT_FAILURE;
258  }
259  else
260  {
261  sConfigurationXML = argv[optind];
262 
263  }
264 
265  doConfigure(sConfigurationXML);
266 
267  if(bDaemonize)
268  {
269  if(sLogFile.empty() && !bSysLog && iLogLevel != 0)
270  {
271  std::cerr<<"unable to daemonize log level must be 0 when logging to stdout"<<std::endl;;
272  return EXIT_FAILURE;
273  }
274 
275  if(daemon(1,0))
276  {
277  std::cerr<<"unable to daemonize"<<std::endl;
278  return EXIT_FAILURE;
279  }
280  }
281 
282  if(!sLogFile.empty())
283  {
284  logger.redirectLogsToFile(sLogFile.c_str());
285  }
286 
287  if(iLogLevel > 0)
288  {
289  logger.setLogLevel(static_cast<EMANE::LogLevel>(iLogLevel));
290 
291  logger.open();
292  }
293  else
294  {
295  logger.setLogLevel(EMANE::NOLOG_LEVEL);
296  }
297 
298 
299  std::stringstream ss;
300  for(int i = 0; i < argc; ++i)
301  {
302  ss<<argv[i]<<" ";
303  }
304 
305  char uuidBuf[37];
306  uuid_unparse(uuid_,uuidBuf);
307 
308  logger.log(EMANE::INFO_LEVEL,"application: %s",ss.str().c_str());
309  logger.log(EMANE::INFO_LEVEL,"application uuid: %s",uuidBuf);
310 
311  if(bRealtime)
312  {
313  struct sched_param schedParam{iPriority};
314 
315  if(sched_setscheduler(0,SCHED_RR,&schedParam))
316  {
317  if(bSysLog || !sLogFile.empty() || !iLogLevel)
318  {
319  std::cerr<<"unable to set realtime scheduler"<<std::endl;
320  }
321 
322  logger.log(EMANE::ABORT_LEVEL,"unable to set realtime scheduler");
323 
324  return EXIT_FAILURE;
325  }
326  }
327  else
328  {
329  if(bSysLog || !sLogFile.empty() || iLogLevel < 2)
330  {
331  std::cerr<<"Please consider using the realtime scheduler to improve fidelity."<<std::endl;
332  }
333 
334  logger.log(EMANE::ERROR_LEVEL,"Please consider using the realtime scheduler to improve fidelity.");
335  }
336 
337 
338  if(!sPIDFile.empty())
339  {
340  std::ofstream pidFile{sPIDFile.c_str(), std::ios::out};
341 
342  if(pidFile)
343  {
344  pidFile<<getpid()<<std::endl;
345  }
346  else
347  {
348  return EXIT_FAILURE;
349  }
350  }
351 
352  if(!sUUIDFile.empty())
353  {
354  std::ofstream uuidFile{sUUIDFile.c_str(), std::ios::out};
355 
356  if(uuidFile)
357  {
358  uuidFile<<uuidBuf<<std::endl;
359  }
360  else
361  {
362  return EXIT_FAILURE;
363  }
364  }
365 
366 
367  doStart();
368 
369 
370  struct sigaction action;
371 
372  memset(&action,0,sizeof(action));
373 
374  action.sa_handler = sighandler;
375 
376  sigaction(SIGINT,&action,nullptr);
377  sigaction(SIGQUIT,&action,nullptr);
378 
379  mutex.lock();
380 
381  // signal handler unlocks mutex
382  mutex.lock();
383 
384  doStop();
385 
386  doDestroy();
387  }
388  catch(const EMANE::Exception & exp)
389  {
390  logger.log(EMANE::ABORT_LEVEL,"%s: %s",exp.type(),exp.what());
391 
392  return EXIT_FAILURE;
393  }
394  catch(const std::exception & exp)
395  {
396  logger.log(EMANE::ABORT_LEVEL,"%s",exp.what());
397 
398  return EXIT_FAILURE;
399  }
400 
401  return EXIT_SUCCESS;
402 }
Main(const std::string &sName)
Definition: main.cc:96
virtual void doStart()=0
void usage()
Definition: emaneinfo.cc:274
virtual const char * type() const
Definition: exception.h:72
Exception base class that allows for type and description information.
Definition: exception.h:49
virtual void doDestroy()=0
virtual void doConfigure(const std::string &)=0
An instance of the EMANE logger. Provides methods for logger configuration and logging.
Definition: logger.h:51
const char * what() const
Definition: exception.h:62
virtual std::vector< std::string > doGetOptionsUsage() const
Definition: main.h:74
virtual std::vector< option > doGetOptions() const
Definition: main.h:69
virtual const uuid_t & getUUID() const
Definition: main.cc:110
virtual void doStop()=0
int main(int argc, char *argv[])
Definition: main.cc:115
virtual std::string doGetOptString() const
Definition: main.h:79
virtual bool doProcessOption(int iOptOpt, const char *pzOptArg)
Definition: main.h:84
std::int32_t toINT32(std::int32_t i32Min=std::numeric_limits< std::int32_t >::min(), std::int32_t i32Max=std::numeric_limits< std::int32_t >::max()) const
Parameter conversion class with range checks.