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