otsdaq_utilities  v2_04_00
ControlsDashboardSupervisor.cc
1 #include "otsdaq-utilities/ControlsDashboard/ControlsDashboardSupervisor.h"
2 #include <dirent.h> //for DIR
3 #include <dirent.h> //for DIR
4 #include <sys/stat.h> //for stat() quickly checking if file exists
5 #include <sys/stat.h> //for stat() quickly checking if file exists
6 #include <thread> //for std::thread
7 
8 #include "otsdaq-core/PluginMakers/MakeSlowControls.h"
9 #include "otsdaq-core/SlowControlsCore/SlowControlsVInterface.h"
10 
11 using namespace ots;
12 
13 #define CONTROLS_SUPERVISOR_DATA_PATH \
14  std::string(__ENV__("SERVICE_DATA_PATH")) + "/ControlsDashboardData/"
15 #define PAGES_DIRECTORY CONTROLS_SUPERVISOR_DATA_PATH + "pages/"
16 
17 XDAQ_INSTANTIATOR_IMPL(ControlsDashboardSupervisor)
18 
19 //========================================================================================================================
20 ControlsDashboardSupervisor::ControlsDashboardSupervisor(xdaq::ApplicationStub* stub)
21  : CoreSupervisorBase(stub)
22 {
23  __SUP_COUT__ << "Constructor." << __E__;
24 
25  INIT_MF("ControlsDashboardSupervisor");
26 
27  // make controls dashboard supervisor directories in case they don't exist
28  mkdir(((std::string)(CONTROLS_SUPERVISOR_DATA_PATH)).c_str(), 0755);
29  mkdir(((std::string)(PAGES_DIRECTORY)).c_str(), 0755);
30 
31  init();
32 
33  __SUP_COUT__ << "Constructed." << __E__;
34 } // end constructor
35 
36 //========================================================================================================================
37 ControlsDashboardSupervisor::~ControlsDashboardSupervisor(void)
38 {
39  __SUP_COUT__ << "Destructor." << __E__;
40  destroy();
41  __SUP_COUT__ << "Destructed." << __E__;
42 } // end destructor()
43 
44 //========================================================================================================================
45 void ControlsDashboardSupervisor::destroy(void)
46 {
47  // called by destructor
48  delete interface_;
49 } // end destroy()
50 
51 //========================================================================================================================
52 void ControlsDashboardSupervisor::init(void)
53 // called by constructor
54 {
55  UID_ = 0;
56 
57  __SUP_COUT__ << std::endl;
58  ConfigurationTree node = CorePropertySupervisorBase::getSupervisorTableNode();
59  std::string pluginType = node.getNode("ControlsInterfacePluginType").getValue();
60  __SUP_COUTV__(pluginType);
61 
62  interface_ =
63  makeSlowControls(pluginType,
64  node.getUIDAsString(),
65  CorePropertySupervisorBase::getContextTreeNode(),
66  CorePropertySupervisorBase::getSupervisorConfigurationPath());
67  __COUT__ << std::endl;
68 
69  __SUP_COUT__ << "Finished init() w/ interface: " << pluginType << std::endl;
70 
71  // interface_->initialize();
72  std::thread(
74 
75  // lockout the messages array for the remainder of the scope
76  // this guarantees the reading thread can safely access the messages
77  std::lock_guard<std::mutex> lock(cs->pluginBusyMutex_);
78 
79  cs->interface_->initialize();
80 
81  },
82  this)
83  .detach(); // thread completes after creating, subscribing, and getting
84  // parameters for all pvs
85 
86 } // end init()
87 
88 //========================================================================================================================
89 // setSupervisorPropertyDefaults
90 // override to set defaults for supervisor property values (before user settings
91 // override)
92 void ControlsDashboardSupervisor::setSupervisorPropertyDefaults()
93 {
94  CorePropertySupervisorBase::setSupervisorProperty(
95  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.CheckUserLockRequestTypes, "*");
96 }
97 
98 //========================================================================================================================
99 // forceSupervisorPropertyValues
100 // override to force supervisor property values (and ignore user settings)
101 void ControlsDashboardSupervisor::forceSupervisorPropertyValues()
102 {
103  CorePropertySupervisorBase::setSupervisorProperty(
104  CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.AutomatedRequestTypes, "poll");
105 }
106 
107 //========================================================================================================================
108 void ControlsDashboardSupervisor::request(const std::string& requestType,
109  cgicc::Cgicc& cgiIn,
110  HttpXmlDocument& xmlOut,
111  const WebUsers::RequestUserInfo& userInfo)
112 {
113  // __SUP_COUT__ << std::endl;
114  // cgicc::Cgicc cgi(in);
115  // __SUP_COUT__ << std::endl;
116  // std::string requestType = CgiDataUtilities::getData(cgi,"RequestType");
117  // __SUP_COUT__ << request << std::endl;
118  // __SUP_COUT__ << this->getApplicationDescriptor()->getLocalId() << " " <<
119  //requestType << " : end"<< std::endl;
120 
121  // if(requestType == "")
122  // {
123  // Default(in, out);
124  // return;
125  // }
126  //
127  // HttpXmlDocument xmldoc;
128  // uint64_t activeSessionIndex;
129  // std::string user;
130  // uint8_t userPermissions;
131  //
132  // //**** start LOGIN GATEWAY CODE ***//
133  // {
134  // bool automaticCommand = requestType == "poll"; //automatic commands should not
135  //refresh cookie code.. only user initiated commands should! bool checkLock =
136  //true; bool getUser = false; bool requireLock = false;
137  //
138  // if(!theRemoteWebUsers_.xmlRequestToGateway(
139  // cgi,
140  // out,
141  // &xmldoc,
142  // allSupervisorInfo_,
143  // &userPermissions, //acquire user's access level (optionally null
144  //pointer) !automaticCommand, //true/false refresh cookie code
145  // 1, //set access level requirement to pass gateway
146  // checkLock, //true/false enable check that system is unlocked or
147  //this user has the lock
148  // requireLock, //true/false requires this user has the lock to
149  //proceed
150  // 0,//&userWithLock, //acquire username with lock (optionally null
151  //pointer)
152  // //(getUser?&user:0), //acquire username of this user (optionally null
153  //pointer) &username,
154  // 0, //acquire user's Display Name
155  // &activeSessionIndex //acquire user's session index associated with the
156  //cookieCode
157  // ))
158  // { //failure
159  // __SUP_COUT__ << "Failed Login Gateway: " <<
160  // out->str() << std::endl; //print out return string on failure
161  // return;
162  // }
163  // }
164  // //**** end LOGIN GATEWAY CODE ***//
165  //
166  //
167  try
168  {
169  if(requestType != "getPages" && !pluginBusyMutex_.try_lock())
170  {
171  __SUP_SS__ << "Controls plugin is still initializing. Please try again in a "
172  "few minutes!"
173  << __E__;
174  __SUP_SS_THROW__;
175  }
176 
177  __SUP_COUT__ << "User name is " << userInfo.username_ << "." << __E__;
178  __SUP_COUT__ << "User permission level for request '" << requestType << "' is "
179  << unsigned(userInfo.permissionLevel_) << "." << __E__;
180 
181  // handle request per requestType
182  handleRequest(requestType, xmlOut, cgiIn, userInfo.username_);
183  }
184  catch(const std::runtime_error& e)
185  {
186  __SUP_SS__ << "Error occurred handling request '" << requestType
187  << "': " << e.what() << __E__;
188  __SUP_COUT__ << ss.str();
189  xmlOut.addTextElementToData("Error", ss.str());
190  }
191  catch(...)
192  {
193  __SS__ << "Unknown error occurred handling request '" << requestType << "!'"
194  << __E__;
195  __SUP_COUT__ << ss.str();
196  xmlOut.addTextElementToData("Error", ss.str());
197  }
198 
199  pluginBusyMutex_.unlock();
200 
201 } // end request()
202 //========================================================================================================================
203 void ControlsDashboardSupervisor::handleRequest(const std::string Command,
204  HttpXmlDocument& xmlOut,
205  cgicc::Cgicc& cgiIn,
206  const std::string& username)
207 {
208  // return xml doc holding server response
209  __SUP_COUT__ << std::endl;
210 
211  if(Command == "poll")
212  {
213  std::string uid = CgiDataUtilities::getOrPostData(cgiIn, "uid");
214  Poll(cgiIn, xmlOut, uid);
215  }
216  else if(Command == "generateUID")
217  {
218  std::string pvList = CgiDataUtilities::getOrPostData(cgiIn, "PVList");
219  GenerateUID(cgiIn, xmlOut, pvList);
220  }
221  else if(Command == "GetPVSettings")
222  {
223  std::string pvList = CgiDataUtilities::getOrPostData(cgiIn, "PVList");
224  GetPVSettings(cgiIn, xmlOut, pvList);
225  xmlOut.addTextElementToData("id", CgiDataUtilities::getData(cgiIn, "id"));
226  }
227  else if(Command == "getList")
228  {
229  __SUP_COUT__ << "PV List requested from server! " << std::endl;
230  GetList(cgiIn, xmlOut);
231  }
232  else if(Command == "getPages")
233  {
234  __SUP_COUT__ << "Requesting pages from server! " << std::endl;
235  GetPages(cgiIn, xmlOut);
236  }
237  else if(Command == "loadPage")
238  {
239  std::string page = CgiDataUtilities::getData(cgiIn, "Page");
240  __SUP_COUT__ << this->getApplicationDescriptor()->getLocalId() << " " << page
241  << std::endl;
242 
243  loadPage(cgiIn, xmlOut, page);
244  }
245  else if(Command == "savePage")
246  {
247  std::string pageName = CgiDataUtilities::getData(cgiIn, "PageName");
248  std::string page = CgiDataUtilities::getOrPostData(cgiIn, "Page");
249  SavePage(cgiIn, xmlOut, pageName, page);
250  }
251  __SUP_COUT__ << "" << std::endl;
252 
253  // xmlOut.outputXmlDocument((std::ostringstream*) out, true);
254 } // end handleRequest
255 
256 //========================================================================================================================
257 void ControlsDashboardSupervisor::Poll(cgicc::Cgicc& cgiIn,
258  HttpXmlDocument& xmlOut,
259  std::string UID)
260 {
261  __SUP_COUT__ << this->getApplicationDescriptor()->getLocalId() << " "
262  << "Polling on UID:" << UID << std::endl;
263 
264  std::map<int, std::set<std::string>>::iterator mapReference;
265 
266  if(UID != "" &&
267  (mapReference = pvDependencyLookupMap_.find(std::stoi(UID))) !=
268  pvDependencyLookupMap_.end()) // We have their current list of PV Dependencies
269  {
270  std::string JSONMessage = "{ ";
271 
272  for(auto pv : mapReference->second)
273  {
274  __SUP_COUT__ << pv << std::endl;
275  // PVInfo * pvInfo = interface_->mapOfPVInfo_.find(pv)->second;
276  //__SUP_COUT__ << pv << ":" << (pvInfo?"Good":"Bad") << std::endl;
277  // interface_->getCurrentPVValue(pv);
278  std::array<std::string, 4> pvInformation = interface_->getCurrentValue(pv);
279  __SUP_COUT__ << pv << ": " << pvInformation[1] << " : " << pvInformation[3]
280  << std::endl;
281 
282  if(pvInformation[0] != "NO_CHANGE")
283  {
284  //__SUP_COUT__ << "Reached" << std::endl;
285  JSONMessage += "\"" + pv + "\": {";
286 
287  /*if(pvInfo->mostRecentBufferIndex - 1 < 0)
288  {
289  std::string value = pvInfo->dataCache[pvInfo->dataCache.size()].second
290  std::string time =
291  }*/
292 
293  JSONMessage += "\"Timestamp\":\"" + pvInformation[0] + "\",";
294  JSONMessage += "\"Value\":\"" + pvInformation[1] + "\",";
295  JSONMessage += "\"Status\":\"" + pvInformation[2] + "\",";
296  JSONMessage += "\"Severity\":\"" + pvInformation[3] + "\"},";
297  }
298  else
299  {
300  __SUP_COUT__ << "No change in value since last poll: " << pv << std::endl;
301  }
302 
303  // Handle Channels that disconnect, etc
304  if(pvInformation[3] == "INVALID")
305  {
306  interface_->subscribe(pv);
307  }
308 
309  //__SUP_COUT__ << pv << ":" << (pvInfo?"Good":"Bad") << std::endl;
310  //__SUP_COUT__ << pv << ":" << pvInfo->mostRecentBufferIndex -1 << std::endl;
311  //__SUP_COUT__ << pv << " : " <<
312  //pvInfo->dataCache[(pvInfo->mostRecentBufferIndex -1)].second << std::endl;
313  }
314 
315  JSONMessage = JSONMessage.substr(0, JSONMessage.length() - 1);
316  JSONMessage += "}";
317  __SUP_COUT__ << JSONMessage << std::endl;
318  xmlOut.addTextElementToData("JSON", JSONMessage); // add to response
319 
320  /*for (std::set<unsigned long>::iterator it = mapReference->second->begin(); it !=
321  mapReference->second.end(); ++it)
322  {
323  //__SUP_COUT__ << this->getApplicationDescriptor()->getLocalId() << it <<
324  std::endl;
325 
326 
327  }*/
328  /*std::string fakeData = std::string("{")
329  + "\"Mu2e_CompStatus_daq01/system_temperature\":
330  \"40.5\","
331  + "\"Mu2e_CompStatus_daq01/load_one\": \"378.2\","
332  + "\"Mu2e_Weather_2/timestamp\": \"11.14.45.2016.4.8\","
333  + "\"Mu2e_CompStatus_daq01/system_temperature\":
334  \"43.4\","
335  + "\"Mu2e_CompStatus_daq01/load_one\":\"80\","
336  + "\"Mu2e_Weather_2/timestamp\": \"11.14.45.2016.4.8\""
337  + "}";
338  xmlOut.addTextElementToData("JSON", fakeData); //add to response*/
339  }
340  else // UID is not in our map so force them to generate a new one
341  {
342  xmlOut.addTextElementToData("JSON",
343  "{ \"message\": \"NOT_FOUND\"}"); // add to response
344  }
345 }
346 //========================================================================================================================
347 void ControlsDashboardSupervisor::GetPVSettings(cgicc::Cgicc& cgiIn,
348  HttpXmlDocument& xmlOut,
349  std::string pvList)
350 {
351  __SUP_COUT__ << this->getApplicationDescriptor()->getLocalId() << " "
352  << "Getting settings for " << pvList << std::endl;
353 
354  std::string JSONMessage = "{ ";
355 
356  std::string pv;
357  size_t pos = 0;
358  size_t nextPos;
359  size_t lastIndex = pvList.find_last_of(",");
360  std::cout << "**********************" << pvList.size() << std::endl;
361  if(pvList.size() > 0)
362  {
363  while((nextPos = pvList.find(",", pos)) != std::string::npos)
364  {
365  pv = pvList.substr(pos, nextPos - pos);
366 
367  __SUP_COUT__ << pv << std::endl;
368 
369  std::array<std::string, 9> pvSettings = interface_->getSettings(pv);
370 
371  JSONMessage += "\"" + pv + "\": {";
372  JSONMessage += "\"Units \": \"" + pvSettings[0] + "\",";
373  JSONMessage += "\"Upper_Display_Limit\": \"" + pvSettings[1] + "\",";
374  JSONMessage += "\"Lower_Display_Limit\": \"" + pvSettings[2] + "\",";
375  JSONMessage += "\"Upper_Alarm_Limit \": \"" + pvSettings[3] + "\",";
376  JSONMessage += "\"Upper_Warning_Limit\": \"" + pvSettings[4] + "\",";
377  JSONMessage += "\"Lower_Warning_Limit\": \"" + pvSettings[5] + "\",";
378  JSONMessage += "\"Lower_Alarm_Limit \": \"" + pvSettings[6] + "\",";
379  JSONMessage += "\"Upper_Control_Limit\": \"" + pvSettings[7] + "\",";
380  JSONMessage += "\"Lower_Control_Limit\": \"" + pvSettings[8] + "\"},";
381 
382  pos = nextPos + 1;
383  }
384 
385  JSONMessage = JSONMessage.substr(0, JSONMessage.length() - 1);
386  JSONMessage += "}";
387 
388  __SUP_COUT__ << JSONMessage << std::endl;
389  xmlOut.addTextElementToData("JSON", JSONMessage); // add to response
390  }
391  else
392  {
393  xmlOut.addTextElementToData(
394  "JSON", "{ \"message\": \"GetPVSettings\"}"); // add to response
395  }
396 }
397 //========================================================================================================================
398 void ControlsDashboardSupervisor::GenerateUID(cgicc::Cgicc& cgiIn,
399  HttpXmlDocument& xmlOut,
400  std::string pvlist)
401 {
402  __SUP_COUT__ << this->getApplicationDescriptor()->getLocalId() << " "
403  << "Generating UID" << std::endl;
404 
405  std::set<std::string> pvDependencies;
406  std::string uid;
407  std::string pv;
408  size_t pos = 0;
409  size_t nextPos;
410  size_t lastIndex = pvlist.find_last_of(",");
411 
412  if(pvlist.size() > 0)
413  {
414  // pvlist.substr(2);
415  __SUP_COUT__ << pvlist << std::endl;
416 
417  while((nextPos = pvlist.find(",", pos)) != std::string::npos)
418  {
419  pv = pvlist.substr(pos, nextPos - pos);
420  //__SUP_COUT__ << UID_ << ":" << pos << "-" << nextPos << " ->" << pv <<
421  // std::endl;
422  pvDependencies.insert(pv);
423  pos = nextPos + 1;
424  }
425 
426  pvDependencyLookupMap_.insert(
427  std::pair<int, std::set<std::string>>(++UID_, pvDependencies));
428 
429  uid = (std::string("{ \"message\": \"") + std::to_string(UID_) + "\"}");
430  }
431  else
432  {
433  __SUP_COUT__ << this->getApplicationDescriptor()->getLocalId()
434  << " PVList invalid: " << pvlist << std::endl;
435  uid = "{ \"message\": \"-1\"}";
436  }
437 
438  __SUP_COUT__ << this->getApplicationDescriptor()->getLocalId() << " NEW UID: " << UID_
439  << std::endl;
440 
441  xmlOut.addTextElementToData("JSON", uid); // add to response
442 }
443 //========================================================================================================================
444 void ControlsDashboardSupervisor::GetList(cgicc::Cgicc& cgiIn, HttpXmlDocument& xmlOut)
445 {
446  if(interface_ != NULL)
447  {
448  __SUP_COUT__ << "Interface is defined! Attempting to get list!" << std::endl;
449  // __SUP_COUT__ << this->getApplicationDescriptor()->getLocalId() << std::endl;
450  std::cout << " " << interface_->getList("JSON") << std::endl;
451 
452  xmlOut.addTextElementToData("JSON",
453  interface_->getList("JSON")); // add to response
454  }
455  else
456  {
457  __SUP_COUT__ << "Interface undefined! Failed to get list!" << std::endl;
458  xmlOut.addTextElementToData("JSON", "[\"None\"]");
459  }
460 }
461 //========================================================================================================================
462 void ControlsDashboardSupervisor::GetPages(cgicc::Cgicc& cgiIn, HttpXmlDocument& xmlOut)
463 {
464  /*DIR * dir;
465  struct dirent * ent;
466  std::string pathToPages = PAGES_DIRECTORY;
467 
468  std::vector<std::string> pages;
469 
470  __SUP_COUT__ << this->getApplicationDescriptor()->getLocalId() << "Path to pages: " <<
471  pathToPages << std::endl; if((dir = opendir (pathToPages.c_str())) != NULL)
472  {
473  while((ent = readdir(dir)) != NULL)
474  {
475  pages.push_back(ent->d_name);
476  __SUP_COUT__ << this->getApplicationDescriptor()->getLocalId() << " GetPages"
477  << ent->d_name << std::endl;
478  }
479  closedir(dir);
480  }
481  else
482  {
483  __SUP_COUT__ << this->getApplicationDescriptor()->getLocalId() << "Could not open
484  directory: " << pathToPages << std::endl; return;
485  }*/
486  std::vector<std::string> pages;
487 
488  listFiles("", true, &pages);
489 
490  std::string returnJSON = "[";
491  for(auto it = pages.begin(); it != pages.end(); it++)
492  {
493  if(*it != "." && *it != "..")
494  returnJSON += "\"" + *it + "\", ";
495  }
496  if(returnJSON.size() > 2 && returnJSON.compare("[") != 0)
497  {
498  __SUP_COUT__ << "Found pages on server!" << std::endl;
499  returnJSON.resize(returnJSON.size() - 2);
500  returnJSON += "]";
501  }
502  else
503  {
504  // No pages on the server
505  __SUP_COUT__ << "No pages found on server!" << std::endl;
506  returnJSON = "[\"None\"]";
507  }
508  std::cout << returnJSON << std::endl;
509 
510  xmlOut.addTextElementToData("JSON", returnJSON); // add to response
511 }
512 //========================================================================================================================
513 void ControlsDashboardSupervisor::loadPage(cgicc::Cgicc& cgiIn,
514  HttpXmlDocument& xmlOut,
515  std::string page)
516 {
517  // FIXME Filter out malicious attacks i.e. ../../../../../ stuff
518  struct stat buffer;
519  if(page.find("..") != std::string::npos)
520  {
521  __SUP_COUT__ << this->getApplicationDescriptor()->getLocalId()
522  << "Error! Request using '..': " << page << std::endl;
523  }
524  else if(page.find("~") != std::string::npos)
525  {
526  __SUP_COUT__ << this->getApplicationDescriptor()->getLocalId()
527  << "Error! Request using '~': " << page << std::endl;
528  }
529  else if(!(stat(page.c_str(), &buffer) == 0))
530  {
531  __SUP_COUT__ << this->getApplicationDescriptor()->getLocalId()
532  << "Error! File not found: " << page << std::endl;
533  }
534 
535  std::string file = PAGES_DIRECTORY;
536  file += "/" + page;
537  __SUP_COUT__ << this->getApplicationDescriptor()->getLocalId()
538  << "Trying to load page: " << page << std::endl;
539  __SUP_COUT__ << this->getApplicationDescriptor()->getLocalId()
540  << "Trying to load page: " << file << std::endl;
541  // read file
542  // for each line in file
543 
544  std::ifstream infile(file);
545  std::cout << "Reading file" << std::endl;
546  std::string JSONpage = "";
547  for(std::string line; getline(infile, line);)
548  {
549  std::cout << line << std::endl;
550  JSONpage += line;
551  }
552  std::cout << "Finished reading file" << std::endl;
553 
554  xmlOut.addTextElementToData("JSON", JSONpage); // add to response
555 }
556 //========================================================================================================================
557 void ControlsDashboardSupervisor::SavePage(cgicc::Cgicc& cgiIn,
558  HttpXmlDocument& xmlOut,
559  std::string pageName,
560  std::string page)
561 {
562  std::string file = PAGES_DIRECTORY;
563  file += pageName;
564 
565  std::string extension = file.substr(file.length() - 4, 4);
566  if(extension != ".dat")
567  {
568  __SUP_COUT__ << "Extension : " << extension << std::endl;
569  file += std::string(".dat");
570  }
571  __SUP_COUT__ << this->getApplicationDescriptor()->getLocalId()
572  << "Trying to save page: " << page << std::endl;
573  __SUP_COUT__ << this->getApplicationDescriptor()->getLocalId()
574  << "Trying to save page as: " << file << std::endl;
575  // read file
576  // for each line in file
577 
578  std::ofstream outputFile;
579  outputFile.open(file);
580 
581  outputFile << page;
582  outputFile.close();
583 
584  std::cout << "Finished writing file" << std::endl;
585 
586  return;
587 }
588 //========================================================================================================================
589 void ControlsDashboardSupervisor::Subscribe(cgicc::Cgicc& cgiIn, HttpXmlDocument& xmlOut)
590 {
591 }
592 //========================================================================================================================
593 void ControlsDashboardSupervisor::Unsubscribe(cgicc::Cgicc& cgiIn,
594  HttpXmlDocument& xmlOut)
595 {
596 }
597 //========================================================================================================================
598 //========================================================================================================================
599 //================================================== UTILITIES
600 //===========================================================
601 //========================================================================================================================
602 bool ControlsDashboardSupervisor::isDir(std::string dir)
603 {
604  struct stat fileInfo;
605  stat(dir.c_str(), &fileInfo);
606  if(S_ISDIR(fileInfo.st_mode))
607  {
608  return true;
609  }
610  else
611  {
612  return false;
613  }
614 }
615 //========================================================================================================================
616 void ControlsDashboardSupervisor::listFiles(std::string baseDir,
617  bool recursive,
618  std::vector<std::string>* pages)
619 {
620  std::string base = PAGES_DIRECTORY;
621  base += baseDir;
622 
623  DIR* dp;
624  struct dirent* dirp;
625  if((dp = opendir(base.c_str())) == NULL)
626  {
627  std::cout << "[ERROR: " << errno << " ] Couldn't open " << base << "."
628  << std::endl;
629  return;
630  }
631  else
632  {
633  while((dirp = readdir(dp)) != NULL)
634  {
635  if(dirp->d_name != std::string(".") && dirp->d_name != std::string(".."))
636  {
637  if(isDir(base + dirp->d_name) == true && recursive == true)
638  {
639  // pages->push_back(baseDir + dirp->d_name);
640  std::cout << "[DIR]\t" << baseDir << dirp->d_name << "/" << std::endl;
641  listFiles(baseDir + dirp->d_name + "/", true, pages);
642  }
643  else
644  {
645  pages->push_back(baseDir + dirp->d_name);
646  std::cout << "[FILE]\t" << baseDir << dirp->d_name << std::endl;
647  }
648  }
649  }
650  closedir(dp);
651  }
652 }