<commit_before>///////////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 2007, mjackson
//  All rights reserved.
//  BSD License: http://www.opensource.org/licenses/bsd-license.html
//
//  This code was written under United States Air Force Contract number
//                           FA8650-04-C-5229
//
///////////////////////////////////////////////////////////////////////////////
#include "Headers/MXATypes.h"
#include "Headers/MXATypeDefs.h"
#include "Interfaces/IDataImportDelegate.h"
#include "Interfaces/IFileIODelegate.h"
#include "MXADataModel/MXADataModel.h"
#include "MXADataModel/MXADataDimension.h"
#include "MXADataModel/MXADataRecord.h"
#include "MXADataModel/MXADataImport.h"
#include "MXADataModel/MXADataSource.h"

#include "HDF5/H5Lite.h"

//-- Boost Unit Testing Framework
#include <boost/test/unit_test.hpp>

using boost::unit_test::test_suite;

// -----------------------------------------------------------------------------
//  Define where to put our temporary files
// -----------------------------------------------------------------------------
#if defined (_WIN32)
  #define FILE_NAME_BEFORE "C:\\WINDOWS\\Temp\\DataImportTest-Before.h5"
  #define FILE_NAME_AFTER "C:\\WINDOWS\\Temp\\DataImportTest-After.h5"
#else
  #define FILE_NAME_BEFORE "/tmp/DataImportTest-Before.h5"
  #define FILE_NAME_AFTER "/tmp/DataImportTest-After.h5"
#endif

//static int GLOBAL_INT = 0;
// -----------------------------------------------------------------------------
//  This class is used to show simply how to write some data into an HDF5 file.
//  Some of the features of the IDataImportDelegate are not shown. This class
//  will simply write a single value to the HDF5 file.
// -----------------------------------------------------------------------------
class H5ImportTestDelegate: public IDataImportDelegate
{
public:
  H5ImportTestDelegate(){};
  virtual ~H5ImportTestDelegate(){};

  int32 importDataSource(MXADataSourcePtr dataSource, MXADataModelPtr model)
  {
      std::string path ( dataSource->generateInternalPath() );
      uint32 pos = path.find_last_of("/");
      std::string parentPath ( path.substr(0, pos)  );
      int32 value = 55;
      hid_t fileId = model->getIODelegate()->getOpenFileId();
      model->getIODelegate()->createGroupsFromPath(parentPath,  fileId);
      //Write the Data to the HDF5 File
    return H5Lite::writeDataset(fileId, path, value, H5Lite::HDFTypeForPrimitive(value));
  }

private:
  H5ImportTestDelegate(const H5ImportTestDelegate&);   //Copy Constructor Not Implemented
  void operator=(const H5ImportTestDelegate&); //Copy Assignment Not Implemented

};

// -----------------------------------------------------------------------------
//  Creates a DataImport class and some DataSources that then delegate out to
//  an instance of an IDataImportDelegate to do the actual import of data.
// -----------------------------------------------------------------------------
void ImportSimpleData(MXADataModelPtr model, std::string outputFilePath)
{
  //Create the DataImport Class
  MXADataImportPtr dataImport( new MXADataImport() );
  dataImport->setOutputFilePath(outputFilePath);
  dataImport->setDataModel(model);

  // Create an Import Delegate to use for the DataSources
  IDataImportDelegatePtr delegatePtr( new H5ImportTestDelegate());

  // We have two dimensions for this model, create a loop to create data sets for each possible dimension value
  MXADataDimension* dim0 = model->getDataDimension(0); // Get the first Dimension, since there is only one this works
  BOOST_REQUIRE(dim0 != NULL);

  MXADataDimension* dim1 = model->getDataDimension(1);
  BOOST_REQUIRE(dim1 != NULL);

  MXADataRecordPtr record = model->getDataRecordByPath("DataRecordContainer/Test Data/Deep Nested Data");
  BOOST_REQUIRE(NULL != record.get());

  int dim0Start = dim0->getStartValue();
  int dim0End = dim0->getEndValue();
  int dim0Increment = dim0->getIncrement();

  int dim1Start = dim1->getStartValue();
  int dim1End = dim1->getEndValue();
  int dim1Increment = dim1->getIncrement();
  std::cout << "CREATING DATA SOURCES" << std::endl;
  for( int i = dim0Start; i <= dim0End; i += dim0Increment )
  {
    for (int j = dim1Start; j <= dim1End; j = j+ dim1Increment)
    {
      //Create some Data Sources
      MXADataSourcePtr ds( new MXADataSource() );
      std::vector<int32> dimValues;
      dimValues.push_back(i);
      dimValues.push_back(j);
      ds->setDimensionValues(dimValues);
      ds->setDataRecord(record);
      ds->setSourcePath(""); //Since we are doing everything in software, this is unused
      ds->setImportDelegate(delegatePtr);
      ds->setDataModel(model);
      dataImport->addDataSource(ds);
    }
  }

  std::cout << "IMPORTING DATA NOW" << std::endl;
  int32 err = dataImport->import();
  BOOST_REQUIRE(err >= 0);

}

// -----------------------------------------------------------------------------
//  Creates a Data Model to use
// -----------------------------------------------------------------------------
MXADataModelPtr createSimpleModel()
{
	  MXADataModelPtr modelPtr = MXADataModel::New();
	  MXADataModel* model = modelPtr.get();
	  model->setDataRoot(std::string("Our Experiment/Laboratory Data"));
	  model->setFileType(MXA::MXACurrentFileType);
	  model->setFileVersion(MXA::MXACurrentFileVersion);

	  // ---------- Test creation/addition of Data Dimensions
	  MXADataDimensionPtr dim0 = MXADataDimension::New("Dimension 1", "Dim1", 0, 2, 1, 2, 1, 1);
	  model->addDataDimension(dim0);
    MXADataDimensionPtr dim1 = MXADataDimension::New("Dimension 2", "Dim2", 1, 3, 1, 3, 1, 1);
    model->addDataDimension(dim1);

	  //Create Data Records
	  MXADataRecordPtr rec1 = MXADataRecord::New(0, std::string("DataRecordContainer"), std::string("DRC1") );
	  model->addDataRecord(rec1);
	  //Create Data Records with Parents
	  MXADataRecordPtr rec2 = MXADataRecord::New(0, std::string("Test Data"), std::string("Test Data") );
	  model->addDataRecord(rec2, rec1);
    MXADataRecordPtr rec3 = MXADataRecord::New(0, std::string("Deep Nested Data"), std::string("Nested Data") );
    model->addDataRecord(rec3, rec2);

	  //Create the Required MetaData
	  std::map<std::string, std::string> md;
	  md[MXA::MXA_CREATOR_TAG] = "Mike Jackson";
	  md[MXA::MXA_DATE_TAG] = "2006:12:24 15:34.51";
	  md[MXA::MXA_DSET_NAME_TAG] = "Testing Data Import";
	  md[MXA::MXA_DESCRIPTION_TAG] = "Application to test importing data to the data file";
	  md[MXA::MXA_PEDIGREE_TAG] = "Original";
	  md[MXA::MXA_DERIVED_SRC_TAG] = "Original Data Files";
	  md[MXA::MXA_RIGHTS_TAG] = "Unlimited";
	  md[MXA::MXA_RELEASE_TAG] = "90312901291239012390";
	  model->setRequiredMetaData(md);

	  return modelPtr;
}

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
int DataImportTest ()
{
  std::string outputFile(FILE_NAME_BEFORE);
  MXADataModelPtr model = createSimpleModel();
  BOOST_REQUIRE(model->writeModel(outputFile, false) >= 0); //Leave the file open for the import
  ImportSimpleData(model, outputFile);
  return 0;
}

// -----------------------------------------------------------------------------
//  Use Boost unit test framework
// -----------------------------------------------------------------------------
test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] ) {
    test_suite* test= BOOST_TEST_SUITE( "Data Import Tests" );
    test->add( BOOST_TEST_CASE( &DataImportTest), 0);
    return test;
}

<commit_msg>Updated test code to use H5Utilities::createGroupPaths<commit_after>///////////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 2007, mjackson
//  All rights reserved.
//  BSD License: http://www.opensource.org/licenses/bsd-license.html
//
//  This code was written under United States Air Force Contract number
//                           FA8650-04-C-5229
//
///////////////////////////////////////////////////////////////////////////////
#include "Headers/MXATypes.h"
#include "Headers/MXATypeDefs.h"
#include "Interfaces/IDataImportDelegate.h"
#include "Interfaces/IFileIODelegate.h"
#include "MXADataModel/MXADataModel.h"
#include "MXADataModel/MXADataDimension.h"
#include "MXADataModel/MXADataRecord.h"
#include "MXADataModel/MXADataImport.h"
#include "MXADataModel/MXADataSource.h"

#include "HDF5/H5Lite.h"
#include "HDF5/H5Utilities.h"

//-- Boost Unit Testing Framework
#include <boost/test/unit_test.hpp>

using boost::unit_test::test_suite;

// -----------------------------------------------------------------------------
//  Define where to put our temporary files
// -----------------------------------------------------------------------------
#if defined (_WIN32)
  #define FILE_NAME_BEFORE "C:\\WINDOWS\\Temp\\DataImportTest-Before.h5"
  #define FILE_NAME_AFTER "C:\\WINDOWS\\Temp\\DataImportTest-After.h5"
#else
  #define FILE_NAME_BEFORE "/tmp/DataImportTest-Before.h5"
  #define FILE_NAME_AFTER "/tmp/DataImportTest-After.h5"
#endif

//static int GLOBAL_INT = 0;
// -----------------------------------------------------------------------------
//  This class is used to show simply how to write some data into an HDF5 file.
//  Some of the features of the IDataImportDelegate are not shown. This class
//  will simply write a single value to the HDF5 file.
// -----------------------------------------------------------------------------
class H5ImportTestDelegate: public IDataImportDelegate
{
public:
  H5ImportTestDelegate(){};
  virtual ~H5ImportTestDelegate(){};

  int32 importDataSource(MXADataSourcePtr dataSource, MXADataModelPtr model)
  {
      std::string path ( dataSource->generateInternalPath() );
      uint32 pos = path.find_last_of("/");
      std::string parentPath ( path.substr(0, pos)  );
      int32 value = 55;
      hid_t fileId = model->getIODelegate()->getOpenFileId();
      H5Utilities::createGroupsFromPath(parentPath,  fileId);
      //Write the Data to the HDF5 File
    return H5Lite::writeDataset(fileId, path, value, H5Lite::HDFTypeForPrimitive(value));
  }

private:
  H5ImportTestDelegate(const H5ImportTestDelegate&);   //Copy Constructor Not Implemented
  void operator=(const H5ImportTestDelegate&); //Copy Assignment Not Implemented

};

// -----------------------------------------------------------------------------
//  Creates a DataImport class and some DataSources that then delegate out to
//  an instance of an IDataImportDelegate to do the actual import of data.
// -----------------------------------------------------------------------------
void ImportSimpleData(MXADataModelPtr model, std::string outputFilePath)
{
  //Create the DataImport Class
  MXADataImportPtr dataImport( new MXADataImport() );
  dataImport->setOutputFilePath(outputFilePath);
  dataImport->setDataModel(model);

  // Create an Import Delegate to use for the DataSources
  IDataImportDelegatePtr delegatePtr( new H5ImportTestDelegate());

  // We have two dimensions for this model, create a loop to create data sets for each possible dimension value
  MXADataDimension* dim0 = model->getDataDimension(0); // Get the first Dimension, since there is only one this works
  BOOST_REQUIRE(dim0 != NULL);

  MXADataDimension* dim1 = model->getDataDimension(1);
  BOOST_REQUIRE(dim1 != NULL);

  MXADataRecordPtr record = model->getDataRecordByPath("DataRecordContainer/Test Data/Deep Nested Data");
  BOOST_REQUIRE(NULL != record.get());

  int dim0Start = dim0->getStartValue();
  int dim0End = dim0->getEndValue();
  int dim0Increment = dim0->getIncrement();

  int dim1Start = dim1->getStartValue();
  int dim1End = dim1->getEndValue();
  int dim1Increment = dim1->getIncrement();
  std::cout << "CREATING DATA SOURCES" << std::endl;
  for( int i = dim0Start; i <= dim0End; i += dim0Increment )
  {
    for (int j = dim1Start; j <= dim1End; j = j+ dim1Increment)
    {
      //Create some Data Sources
      MXADataSourcePtr ds( new MXADataSource() );
      std::vector<int32> dimValues;
      dimValues.push_back(i);
      dimValues.push_back(j);
      ds->setDimensionValues(dimValues);
      ds->setDataRecord(record);
      ds->setSourcePath(""); //Since we are doing everything in software, this is unused
      ds->setImportDelegate(delegatePtr);
      ds->setDataModel(model);
      dataImport->addDataSource(ds);
    }
  }

  std::cout << "IMPORTING DATA NOW" << std::endl;
  int32 err = dataImport->import();
  BOOST_REQUIRE(err >= 0);

}

// -----------------------------------------------------------------------------
//  Creates a Data Model to use
// -----------------------------------------------------------------------------
MXADataModelPtr createSimpleModel()
{
	  MXADataModelPtr modelPtr = MXADataModel::New();
	  MXADataModel* model = modelPtr.get();
	  model->setDataRoot(std::string("Our Experiment/Laboratory Data"));
	  model->setFileType(MXA::MXACurrentFileType);
	  model->setFileVersion(MXA::MXACurrentFileVersion);

	  // ---------- Test creation/addition of Data Dimensions
	  MXADataDimensionPtr dim0 = MXADataDimension::New("Dimension 1", "Dim1", 0, 2, 1, 2, 1, 1);
	  model->addDataDimension(dim0);
    MXADataDimensionPtr dim1 = MXADataDimension::New("Dimension 2", "Dim2", 1, 3, 1, 3, 1, 1);
    model->addDataDimension(dim1);

	  //Create Data Records
	  MXADataRecordPtr rec1 = MXADataRecord::New(0, std::string("DataRecordContainer"), std::string("DRC1") );
	  model->addDataRecord(rec1);
	  //Create Data Records with Parents
	  MXADataRecordPtr rec2 = MXADataRecord::New(0, std::string("Test Data"), std::string("Test Data") );
	  model->addDataRecord(rec2, rec1);
    MXADataRecordPtr rec3 = MXADataRecord::New(0, std::string("Deep Nested Data"), std::string("Nested Data") );
    model->addDataRecord(rec3, rec2);

	  //Create the Required MetaData
	  std::map<std::string, std::string> md;
	  md[MXA::MXA_CREATOR_TAG] = "Mike Jackson";
	  md[MXA::MXA_DATE_TAG] = "2006:12:24 15:34.51";
	  md[MXA::MXA_DSET_NAME_TAG] = "Testing Data Import";
	  md[MXA::MXA_DESCRIPTION_TAG] = "Application to test importing data to the data file";
	  md[MXA::MXA_PEDIGREE_TAG] = "Original";
	  md[MXA::MXA_DERIVED_SRC_TAG] = "Original Data Files";
	  md[MXA::MXA_RIGHTS_TAG] = "Unlimited";
	  md[MXA::MXA_RELEASE_TAG] = "90312901291239012390";
	  model->setRequiredMetaData(md);

	  return modelPtr;
}

// -----------------------------------------------------------------------------
//
// -----------------------------------------------------------------------------
int DataImportTest ()
{
  std::string outputFile(FILE_NAME_BEFORE);
  MXADataModelPtr model = createSimpleModel();
  BOOST_REQUIRE(model->writeModel(outputFile, false) >= 0); //Leave the file open for the import
  ImportSimpleData(model, outputFile);
  return 0;
}

// -----------------------------------------------------------------------------
//  Use Boost unit test framework
// -----------------------------------------------------------------------------
test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] ) {
    test_suite* test= BOOST_TEST_SUITE( "Data Import Tests" );
    test->add( BOOST_TEST_CASE( &DataImportTest), 0);
    return test;
}

<|endoftext|>


<commit_before>// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
/*
 * Ceph - scalable distributed file system
 *
 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
 *
 * This is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1, as published by the Free Software
 * Foundation.  See file COPYING.
 *
 */

#include "Cond.h"
#include "Mutex.h"
#include "Thread.h"
#include "Timer.h"

#include "config.h"
#include "include/Context.h"

#define DOUT_SUBSYS timer
#undef dout_prefix
#define dout_prefix *_dout << dbeginl << " TIMER "

#define DBL 10

#include <sstream>
#include <signal.h>
#include <sys/time.h>
#include <math.h>

typedef std::multimap < utime_t, Context *> scheduled_map_t;
typedef std::map < Context*, scheduled_map_t::iterator > event_lookup_map_t;

class TimerThread : public Thread {
public:
  TimerThread(Timer &parent_)
    : p(parent_)
  {
  }

  void *entry()
  {
    p.lock.Lock();
    while (true) {
      /* Wait for a cond_signal */
      scheduled_map_t::iterator s = p.scheduled.begin();
      if (s == p.scheduled.end())
	p.cond.Wait(p.lock);
      else
	p.cond.WaitUntil(p.lock, s->first);

      if (p.exiting) {
	dout(DBL) << "exiting TimerThread" << dendl;
	p.lock.Unlock();
	return NULL;
      }

      // Find out what callbacks we have to do
      list <Context*> running;
      utime_t now = g_clock.now();
      p.pop_running(running, now);

      if (running.empty()) {
	dout(DBL) << "TimerThread: nothing to do." << dendl;
	continue;
      }

      p.lock.Unlock();

      lock_event_lock();
      // p.running is protected by the event lock.
      p.running.swap(running);
      while (true) {
	list <Context*>::const_iterator cit = p.running.begin();
	if (cit == p.running.end())
	  break;
	p.running.pop_front();
	Context *ctx = *cit;
	dout(DBL) << "start callback " << ctx << dendl;
	ctx->finish(0);
	dout(DBL) << "deleting callback " << ctx << dendl;
	delete ctx;
	unlock_event_lock();
	// Release the event_lock here to give other waiters a chance.
	dout(DBL) << "finished callback " << ctx << dendl;
	lock_event_lock();
      }
      unlock_event_lock();
      p.lock.Lock();
    }
  }

private:
  inline void lock_event_lock()
  {
    if (p.event_lock)
      p.event_lock->Lock();
  }

  inline void unlock_event_lock()
  {
    if (p.event_lock)
      p.event_lock->Unlock();
  }

  Timer &p;
};

Timer::Timer()
  : lock("Timer::lock"),
    event_lock(NULL),
    cond(),
    thread(NULL),
    exiting(false)
{
  if (init()) {
    assert(0);
  }
}

Timer::Timer(Mutex *event_lock_)
  : lock("Timer::lock"),
    event_lock(event_lock_),
    cond(),
    thread(NULL),
    exiting(false)
{
  if (init()) {
    assert(0);
  }
}

Timer::~Timer()
{
  shutdown();
}

void Timer::shutdown()
{
  lock.Lock();
  if (!thread) {
    lock.Unlock();
    return;
  }
  exiting = true;
  cancel_all_events_impl(false);
  cond.Signal();
  lock.Unlock();

  /* Block until the thread has exited.
   * Only then do we know that no events are in progress. */
  thread->join();

  delete thread;
  thread = NULL;
}

void Timer::add_event_after(double seconds,
                            Context *callback)
{
  utime_t when = g_clock.now();
  when += seconds;
  Timer::add_event_at(when, callback);
}

void Timer::add_event_at(utime_t when, Context *callback)
{
  lock.Lock();

  /* Don't start using the timer until it's initialized */
  assert(thread);

  dout(DBL) << "add_event " << callback << " at " << when << dendl;

  scheduled_map_t::value_type s_val(when, callback);
  scheduled_map_t::iterator i = scheduled.insert(s_val);

  event_lookup_map_t::value_type e_val(callback, i);
  pair < event_lookup_map_t::iterator, bool > rval(events.insert(e_val));
  dout(DBL) << "inserted events entry for " << callback << dendl;

  /* If you hit this, you tried to insert the same Context* twice. */
  assert(rval.second);

  /* If the event we have just inserted comes before everything else, we need to
   * adjust our timeout. */
  if (i == scheduled.begin())
    cond.Signal();

  dout(19) << show_all_events(__func__) << dendl;
  lock.Unlock();
}

bool Timer::cancel_event(Context *callback)
{
  dout(DBL) << __PRETTY_FUNCTION__ << ": " << callback << dendl;

  lock.Lock();
  bool ret = cancel_event_impl(callback, false);
  lock.Unlock();
  return ret;
}

void Timer::cancel_all_events(void)
{
  dout(DBL) << __PRETTY_FUNCTION__ << dendl;

  dout(19) << show_all_events(__func__) << dendl;

  lock.Lock();
  cancel_all_events_impl(false);
  lock.Unlock();
}

int Timer::init()
{
  int ret = 0;
  lock.Lock();
  assert(exiting == false);
  assert(!thread);

  dout(DBL) << "Timer::init: starting thread" << dendl;
  thread = new TimerThread(*this);
  ret = thread->create();
  lock.Unlock();
  return ret;
}

bool Timer::cancel_event_impl(Context *callback, bool cancel_running)
{
  dout(19) << show_all_events(__func__) << dendl;

  event_lookup_map_t::iterator e = events.find(callback);
  if (e != events.end()) {
    // Erase the item out of the scheduled map.
    scheduled.erase(e->second);
    events.erase(e);

    delete callback;
    return true;
  }

  // If we can't peek at the running list, we have to give up.
  if (!cancel_running)
    return false;

  // Ok, we will check the running list. It's safe, because we're holding the
  // event_lock.
  list <Context*>::iterator cit =
    std::find(running.begin(), running.end(), callback);
  if (cit == running.end())
    return false;
  running.erase(cit);
  delete callback;
  return true;
}

void Timer::cancel_all_events_impl(bool clear_running)
{
  while (1) {
    scheduled_map_t::iterator s = scheduled.begin();
    if (s == scheduled.end())
      break;
    delete s->second;
    scheduled.erase(s);
  }
  events.clear();

  if (clear_running) {
    running.clear();
  }
}

void Timer::pop_running(list <Context*> &running_, const utime_t &now)
{
  while (true) {
    std::multimap < utime_t, Context* >::iterator s = scheduled.begin();
    if (s == scheduled.end())
      return;
    const utime_t &utime(s->first);
    Context *cit(s->second);
    if (utime > now)
      return;
    running_.push_back(cit);
    event_lookup_map_t::iterator e = events.find(cit);
    assert(e != events.end());
    events.erase(e);
    scheduled.erase(s);
  }
}

std::string Timer::show_all_events(const char *caller) const
{
  ostringstream oss;
  string sep;
  oss << "show_all_events: from " << caller << ": scheduled [";
  for (scheduled_map_t::const_iterator s = scheduled.begin();
       s != scheduled.end();
       ++s)
  {
    oss << sep << s->first << "->" << s->second;
    sep = ",";
  }
  oss << "] ";

  oss << "events [";
  string sep2;
  for (event_lookup_map_t::const_iterator e = events.begin();
       e != events.end();
       ++e)
  {
    oss << sep2 << e->first << "->" << e->second->first;
    sep2 = ",";
  }
  oss << "]";
  return oss.str();
}

/******************************************************************/
SafeTimer::SafeTimer(Mutex &event_lock)
  : t(&event_lock)
{
}

SafeTimer::~SafeTimer()
{
  dout(DBL) << __PRETTY_FUNCTION__ << dendl;

  t.shutdown();
}

void SafeTimer::shutdown()
{
  t.shutdown();
}

void SafeTimer::add_event_after(double seconds, Context *callback)
{
  assert(t.event_lock->is_locked());

  t.add_event_after(seconds, callback);
}

void SafeTimer::add_event_at(utime_t when, Context *callback)
{
  assert(t.event_lock->is_locked());

  t.add_event_at(when, callback);
}

bool SafeTimer::cancel_event(Context *callback)
{
  dout(DBL) << __PRETTY_FUNCTION__ << ": " << callback << dendl;

  assert(t.event_lock->is_locked());

  t.lock.Lock();
  bool ret = t.cancel_event_impl(callback, true);
  t.lock.Unlock();
  return ret;
}

void SafeTimer::cancel_all_events()
{
  dout(DBL) << __PRETTY_FUNCTION__ << dendl;

  assert(t.event_lock->is_locked());

  t.lock.Lock();
  t.cancel_all_events_impl(true);
  t.lock.Unlock();
}
<commit_msg>TimerThread:don't call pop_front before iter deref<commit_after>// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
/*
 * Ceph - scalable distributed file system
 *
 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
 *
 * This is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1, as published by the Free Software
 * Foundation.  See file COPYING.
 *
 */

#include "Cond.h"
#include "Mutex.h"
#include "Thread.h"
#include "Timer.h"

#include "config.h"
#include "include/Context.h"

#define DOUT_SUBSYS timer
#undef dout_prefix
#define dout_prefix *_dout << dbeginl << " TIMER "

#define DBL 10

#include <sstream>
#include <signal.h>
#include <sys/time.h>
#include <math.h>

typedef std::multimap < utime_t, Context *> scheduled_map_t;
typedef std::map < Context*, scheduled_map_t::iterator > event_lookup_map_t;

class TimerThread : public Thread {
public:
  TimerThread(Timer &parent_)
    : p(parent_)
  {
  }

  void *entry()
  {
    p.lock.Lock();
    while (true) {
      /* Wait for a cond_signal */
      scheduled_map_t::iterator s = p.scheduled.begin();
      if (s == p.scheduled.end())
	p.cond.Wait(p.lock);
      else
	p.cond.WaitUntil(p.lock, s->first);

      if (p.exiting) {
	dout(DBL) << "exiting TimerThread" << dendl;
	p.lock.Unlock();
	return NULL;
      }

      // Find out what callbacks we have to do
      list <Context*> running;
      utime_t now = g_clock.now();
      p.pop_running(running, now);

      if (running.empty()) {
	dout(DBL) << "TimerThread: nothing to do." << dendl;
	continue;
      }

      p.lock.Unlock();

      lock_event_lock();
      // p.running is protected by the event lock.
      p.running.swap(running);
      while (true) {
	list <Context*>::const_iterator cit = p.running.begin();
	if (cit == p.running.end())
	  break;
	Context *ctx = *cit;
	p.running.pop_front();
	dout(DBL) << "start callback " << ctx << dendl;
	ctx->finish(0);
	dout(DBL) << "deleting callback " << ctx << dendl;
	delete ctx;
	unlock_event_lock();
	// Release the event_lock here to give other waiters a chance.
	dout(DBL) << "finished callback " << ctx << dendl;
	lock_event_lock();
      }
      unlock_event_lock();
      p.lock.Lock();
    }
  }

private:
  inline void lock_event_lock()
  {
    if (p.event_lock)
      p.event_lock->Lock();
  }

  inline void unlock_event_lock()
  {
    if (p.event_lock)
      p.event_lock->Unlock();
  }

  Timer &p;
};

Timer::Timer()
  : lock("Timer::lock"),
    event_lock(NULL),
    cond(),
    thread(NULL),
    exiting(false)
{
  if (init()) {
    assert(0);
  }
}

Timer::Timer(Mutex *event_lock_)
  : lock("Timer::lock"),
    event_lock(event_lock_),
    cond(),
    thread(NULL),
    exiting(false)
{
  if (init()) {
    assert(0);
  }
}

Timer::~Timer()
{
  shutdown();
}

void Timer::shutdown()
{
  lock.Lock();
  if (!thread) {
    lock.Unlock();
    return;
  }
  exiting = true;
  cancel_all_events_impl(false);
  cond.Signal();
  lock.Unlock();

  /* Block until the thread has exited.
   * Only then do we know that no events are in progress. */
  thread->join();

  delete thread;
  thread = NULL;
}

void Timer::add_event_after(double seconds,
                            Context *callback)
{
  utime_t when = g_clock.now();
  when += seconds;
  Timer::add_event_at(when, callback);
}

void Timer::add_event_at(utime_t when, Context *callback)
{
  lock.Lock();

  /* Don't start using the timer until it's initialized */
  assert(thread);

  dout(DBL) << "add_event " << callback << " at " << when << dendl;

  scheduled_map_t::value_type s_val(when, callback);
  scheduled_map_t::iterator i = scheduled.insert(s_val);

  event_lookup_map_t::value_type e_val(callback, i);
  pair < event_lookup_map_t::iterator, bool > rval(events.insert(e_val));
  dout(DBL) << "inserted events entry for " << callback << dendl;

  /* If you hit this, you tried to insert the same Context* twice. */
  assert(rval.second);

  /* If the event we have just inserted comes before everything else, we need to
   * adjust our timeout. */
  if (i == scheduled.begin())
    cond.Signal();

  dout(19) << show_all_events(__func__) << dendl;
  lock.Unlock();
}

bool Timer::cancel_event(Context *callback)
{
  dout(DBL) << __PRETTY_FUNCTION__ << ": " << callback << dendl;

  lock.Lock();
  bool ret = cancel_event_impl(callback, false);
  lock.Unlock();
  return ret;
}

void Timer::cancel_all_events(void)
{
  dout(DBL) << __PRETTY_FUNCTION__ << dendl;

  dout(19) << show_all_events(__func__) << dendl;

  lock.Lock();
  cancel_all_events_impl(false);
  lock.Unlock();
}

int Timer::init()
{
  int ret = 0;
  lock.Lock();
  assert(exiting == false);
  assert(!thread);

  dout(DBL) << "Timer::init: starting thread" << dendl;
  thread = new TimerThread(*this);
  ret = thread->create();
  lock.Unlock();
  return ret;
}

bool Timer::cancel_event_impl(Context *callback, bool cancel_running)
{
  dout(19) << show_all_events(__func__) << dendl;

  event_lookup_map_t::iterator e = events.find(callback);
  if (e != events.end()) {
    // Erase the item out of the scheduled map.
    scheduled.erase(e->second);
    events.erase(e);

    delete callback;
    return true;
  }

  // If we can't peek at the running list, we have to give up.
  if (!cancel_running)
    return false;

  // Ok, we will check the running list. It's safe, because we're holding the
  // event_lock.
  list <Context*>::iterator cit =
    std::find(running.begin(), running.end(), callback);
  if (cit == running.end())
    return false;
  running.erase(cit);
  delete callback;
  return true;
}

void Timer::cancel_all_events_impl(bool clear_running)
{
  while (1) {
    scheduled_map_t::iterator s = scheduled.begin();
    if (s == scheduled.end())
      break;
    delete s->second;
    scheduled.erase(s);
  }
  events.clear();

  if (clear_running) {
    running.clear();
  }
}

void Timer::pop_running(list <Context*> &running_, const utime_t &now)
{
  while (true) {
    std::multimap < utime_t, Context* >::iterator s = scheduled.begin();
    if (s == scheduled.end())
      return;
    const utime_t &utime(s->first);
    Context *cit(s->second);
    if (utime > now)
      return;
    running_.push_back(cit);
    event_lookup_map_t::iterator e = events.find(cit);
    assert(e != events.end());
    events.erase(e);
    scheduled.erase(s);
  }
}

std::string Timer::show_all_events(const char *caller) const
{
  ostringstream oss;
  string sep;
  oss << "show_all_events: from " << caller << ": scheduled [";
  for (scheduled_map_t::const_iterator s = scheduled.begin();
       s != scheduled.end();
       ++s)
  {
    oss << sep << s->first << "->" << s->second;
    sep = ",";
  }
  oss << "] ";

  oss << "events [";
  string sep2;
  for (event_lookup_map_t::const_iterator e = events.begin();
       e != events.end();
       ++e)
  {
    oss << sep2 << e->first << "->" << e->second->first;
    sep2 = ",";
  }
  oss << "]";
  return oss.str();
}

/******************************************************************/
SafeTimer::SafeTimer(Mutex &event_lock)
  : t(&event_lock)
{
}

SafeTimer::~SafeTimer()
{
  dout(DBL) << __PRETTY_FUNCTION__ << dendl;

  t.shutdown();
}

void SafeTimer::shutdown()
{
  t.shutdown();
}

void SafeTimer::add_event_after(double seconds, Context *callback)
{
  assert(t.event_lock->is_locked());

  t.add_event_after(seconds, callback);
}

void SafeTimer::add_event_at(utime_t when, Context *callback)
{
  assert(t.event_lock->is_locked());

  t.add_event_at(when, callback);
}

bool SafeTimer::cancel_event(Context *callback)
{
  dout(DBL) << __PRETTY_FUNCTION__ << ": " << callback << dendl;

  assert(t.event_lock->is_locked());

  t.lock.Lock();
  bool ret = t.cancel_event_impl(callback, true);
  t.lock.Unlock();
  return ret;
}

void SafeTimer::cancel_all_events()
{
  dout(DBL) << __PRETTY_FUNCTION__ << dendl;

  assert(t.event_lock->is_locked());

  t.lock.Lock();
  t.cancel_all_events_impl(true);
  t.lock.Unlock();
}
<|endoftext|>


<commit_before>/************************************************
 *  static_vector_test.hpp
 *  bptree
 *
 *  Copyright (c) 2017, Chi-En Wu
 *  Distributed under MIT License
 ************************************************/

#include <cassert>
#include <cstddef>

#include <array>
#include <stdexcept>

#include <gtest/gtest.h>

#include <bptree/internal/static_vector.hpp>

using bptree::internal::static_vector;

enum class constructed_with {
    default_ctor,
    copy_ctor,
    move_ctor,
    value_ctor
};

class custom_type {
 public:  // Public Method(s)
    custom_type()
      : custom_type(constructed_with::default_ctor, 0)
        { /* do nothing */ }

    custom_type(custom_type const& other)
      : custom_type(constructed_with::copy_ctor, other.val_)
        { /* do nothing */ }

    custom_type(custom_type&& other)
      : custom_type(constructed_with::move_ctor, other.val_)
        { other.val_ = 0; }

    explicit custom_type(int n)
      : custom_type(constructed_with::value_ctor, n)
        { /* do nothing */ }

    // used only for generating expected results in tests
    custom_type(constructed_with ctor, int n)
      : ctor_(ctor), val_(n)
        { ++num_instances_; }

    ~custom_type()
        { --num_instances_; }

    custom_type& operator=(custom_type const& other)
        { val_ = other.val_; return *this; }

    custom_type& operator=(custom_type&& other)
        { val_ = other.val_; other.val_ = 0; return *this; }

    bool operator==(custom_type const& other) const
        { return val_ == other.val_ && ctor_ == other.ctor_; }

    int get() const
        { return val_; }

    constructed_with ctor() const
        { return ctor_; }

 public:  // Public Static Method(s)
    static std::size_t num_instances()
        { return num_instances_; }

 private:  // Private Property(ies)
    int val_;
    constructed_with ctor_;

 private:  // Private Static Property(ies)
    static std::size_t num_instances_;
};

std::size_t custom_type::num_instances_ = 0;
std::size_t const SIZE_VECTOR = 10;

template <typename T, std::size_t N>
void assert_static_vector_size(static_vector<T, N> const& v, std::size_t size) {
    assert(size <= N);

    EXPECT_EQ(size == 0, v.empty());
    EXPECT_EQ(size == N, v.full());
    EXPECT_EQ(size, v.size());
    EXPECT_EQ(N, v.max_size());
    EXPECT_EQ(N, v.capacity());

    EXPECT_THROW(v.at(size), std::out_of_range);
}

template <typename T, std::size_t N, typename F>
void assert_static_vector_values(static_vector<T, N> const& v, std::size_t count,
                                 F get_expected_value) {
    assert_static_vector_size(v, count);

    typename static_vector<T, N>::const_pointer data_ptr = v.data();
    for (std::size_t pos = 0; pos < count; ++pos, ++data_ptr) {
        T const& expected = get_expected_value(pos);
        EXPECT_EQ(expected, v[pos]);
        EXPECT_EQ(expected, v.at(pos));
        EXPECT_EQ(expected, *data_ptr);
    }
}

TEST(StaticVectorTest, EmptyVector) {
    static_vector<custom_type, SIZE_VECTOR> v;

    EXPECT_EQ(0, custom_type::num_instances());

    assert_static_vector_size(v, 0);
}

TEST(StaticVectorTest, ConstructWithCount) {
    std::size_t const COUNT = 3;
    static_vector<custom_type, SIZE_VECTOR> v(COUNT);

    EXPECT_EQ(COUNT, custom_type::num_instances());

    auto expected_value = custom_type(constructed_with::default_ctor, 0);
    auto get_expected_value = [&expected_value](std::size_t pos) -> custom_type const &
        { return expected_value; };
    assert_static_vector_values(v, COUNT, get_expected_value);
}

TEST(StaticVectorTest, DestructValues) {
    {
        static_vector<custom_type, SIZE_VECTOR> v(3);
        EXPECT_EQ(3, custom_type::num_instances());
    }

    EXPECT_EQ(0, custom_type::num_instances());
}

TEST(StaticVectorTest, ConstructFullVector) {
    static_vector<custom_type, SIZE_VECTOR> v(SIZE_VECTOR);

    EXPECT_EQ(SIZE_VECTOR, custom_type::num_instances());

    auto expected_value = custom_type(constructed_with::default_ctor, 0);
    auto get_expected_value = [&expected_value](std::size_t pos) -> custom_type const &
        { return expected_value; };
    assert_static_vector_values(v, SIZE_VECTOR, get_expected_value);
}

TEST(StaticVectorTest, ConstructWithCountAndValue) {
    std::size_t const COUNT = 3;
    int const VALUE = 10;
    static_vector<custom_type, SIZE_VECTOR> v(COUNT, custom_type(VALUE));

    EXPECT_EQ(COUNT, custom_type::num_instances());

    auto expected_value = custom_type(constructed_with::copy_ctor, VALUE);
    auto get_expected_value = [&expected_value](std::size_t pos) -> custom_type const &
        { return expected_value; };
    assert_static_vector_values(v, COUNT, get_expected_value);
}

TEST(StaticVectorTest, ConstructWithIteratorPair) {
    std::array<custom_type, 5> arr = {
        custom_type(1),
        custom_type(2),
        custom_type(3),
        custom_type(5),
        custom_type(8)
    };

    static_vector<custom_type, SIZE_VECTOR> v(arr.begin(), arr.end());

    EXPECT_EQ(arr.size() * 2, custom_type::num_instances());

    auto get_expected_value = [&arr](std::size_t pos) -> custom_type
        { return custom_type(constructed_with::copy_ctor, arr[pos].get()); };
    assert_static_vector_values(v, arr.size(), get_expected_value);
}

TEST(StaticVectorTest, ConstructWithInitializerList) {
    std::size_t const SIZE = 5;
    static_vector<custom_type, SIZE_VECTOR> v = {
        custom_type(1),
        custom_type(2),
        custom_type(3),
        custom_type(5),
        custom_type(8)
    };

    EXPECT_EQ(SIZE, custom_type::num_instances());

    int expected_values[] = {1, 2, 3, 5, 8};
    auto get_expected_value = [&expected_values](std::size_t pos) -> custom_type
        { return custom_type(constructed_with::copy_ctor, expected_values[pos]); };
    assert_static_vector_values(v, SIZE, get_expected_value);
}

TEST(StaticVectorTest, ConstructWithAnotherVector) {
    std::size_t const SIZE = 5;
    static_vector<custom_type, SIZE_VECTOR> v1 = {
        custom_type(1),
        custom_type(2),
        custom_type(3),
        custom_type(5),
        custom_type(8)
    };

    static_vector<custom_type, SIZE_VECTOR> v2 = v1;

    EXPECT_EQ(SIZE * 2, custom_type::num_instances());

    int expected_values[] = {1, 2, 3, 5, 8};
    auto get_expected_value = [&expected_values](std::size_t pos) -> custom_type
        { return custom_type(constructed_with::copy_ctor, expected_values[pos]); };
    assert_static_vector_values(v2, SIZE, get_expected_value);
}
<commit_msg>:art: Auto deduce return type of `get_expected_value`<commit_after>/************************************************
 *  static_vector_test.hpp
 *  bptree
 *
 *  Copyright (c) 2017, Chi-En Wu
 *  Distributed under MIT License
 ************************************************/

#include <cassert>
#include <cstddef>

#include <array>
#include <stdexcept>

#include <gtest/gtest.h>

#include <bptree/internal/static_vector.hpp>

using bptree::internal::static_vector;

enum class constructed_with {
    default_ctor,
    copy_ctor,
    move_ctor,
    value_ctor
};

class custom_type {
 public:  // Public Method(s)
    custom_type()
      : custom_type(constructed_with::default_ctor, 0)
        { /* do nothing */ }

    custom_type(custom_type const& other)
      : custom_type(constructed_with::copy_ctor, other.val_)
        { /* do nothing */ }

    custom_type(custom_type&& other)
      : custom_type(constructed_with::move_ctor, other.val_)
        { other.val_ = 0; }

    explicit custom_type(int n)
      : custom_type(constructed_with::value_ctor, n)
        { /* do nothing */ }

    // used only for generating expected results in tests
    custom_type(constructed_with ctor, int n)
      : ctor_(ctor), val_(n)
        { ++num_instances_; }

    ~custom_type()
        { --num_instances_; }

    custom_type& operator=(custom_type const& other)
        { val_ = other.val_; return *this; }

    custom_type& operator=(custom_type&& other)
        { val_ = other.val_; other.val_ = 0; return *this; }

    bool operator==(custom_type const& other) const
        { return val_ == other.val_ && ctor_ == other.ctor_; }

    int get() const
        { return val_; }

    constructed_with ctor() const
        { return ctor_; }

 public:  // Public Static Method(s)
    static std::size_t num_instances()
        { return num_instances_; }

 private:  // Private Property(ies)
    int val_;
    constructed_with ctor_;

 private:  // Private Static Property(ies)
    static std::size_t num_instances_;
};

std::size_t custom_type::num_instances_ = 0;
std::size_t const SIZE_VECTOR = 10;

template <typename T, std::size_t N>
void assert_static_vector_size(static_vector<T, N> const& v, std::size_t size) {
    assert(size <= N);

    EXPECT_EQ(size == 0, v.empty());
    EXPECT_EQ(size == N, v.full());
    EXPECT_EQ(size, v.size());
    EXPECT_EQ(N, v.max_size());
    EXPECT_EQ(N, v.capacity());

    EXPECT_THROW(v.at(size), std::out_of_range);
}

template <typename T, std::size_t N, typename F>
void assert_static_vector_values(static_vector<T, N> const& v, std::size_t count,
                                 F get_expected_value) {
    assert_static_vector_size(v, count);

    typename static_vector<T, N>::const_pointer data_ptr = v.data();
    for (std::size_t pos = 0; pos < count; ++pos, ++data_ptr) {
        T const& expected = get_expected_value(pos);
        EXPECT_EQ(expected, v[pos]);
        EXPECT_EQ(expected, v.at(pos));
        EXPECT_EQ(expected, *data_ptr);
    }
}

TEST(StaticVectorTest, EmptyVector) {
    static_vector<custom_type, SIZE_VECTOR> v;

    EXPECT_EQ(0, custom_type::num_instances());

    assert_static_vector_size(v, 0);
}

TEST(StaticVectorTest, ConstructWithCount) {
    std::size_t const COUNT = 3;
    static_vector<custom_type, SIZE_VECTOR> v(COUNT);

    EXPECT_EQ(COUNT, custom_type::num_instances());

    auto expected_value = custom_type(constructed_with::default_ctor, 0);
    auto get_expected_value = [&expected_value](std::size_t pos) -> decltype(auto)
        { return (expected_value); };
    assert_static_vector_values(v, COUNT, get_expected_value);
}

TEST(StaticVectorTest, DestructValues) {
    {
        static_vector<custom_type, SIZE_VECTOR> v(3);
        EXPECT_EQ(3, custom_type::num_instances());
    }

    EXPECT_EQ(0, custom_type::num_instances());
}

TEST(StaticVectorTest, ConstructFullVector) {
    static_vector<custom_type, SIZE_VECTOR> v(SIZE_VECTOR);

    EXPECT_EQ(SIZE_VECTOR, custom_type::num_instances());

    auto expected_value = custom_type(constructed_with::default_ctor, 0);
    auto get_expected_value = [&expected_value](std::size_t pos) -> decltype(auto)
        { return (expected_value); };
    assert_static_vector_values(v, SIZE_VECTOR, get_expected_value);
}

TEST(StaticVectorTest, ConstructWithCountAndValue) {
    std::size_t const COUNT = 3;
    int const VALUE = 10;
    static_vector<custom_type, SIZE_VECTOR> v(COUNT, custom_type(VALUE));

    EXPECT_EQ(COUNT, custom_type::num_instances());

    auto expected_value = custom_type(constructed_with::copy_ctor, VALUE);
    auto get_expected_value = [&expected_value](std::size_t pos) -> decltype(auto)
        { return (expected_value); };
    assert_static_vector_values(v, COUNT, get_expected_value);
}

TEST(StaticVectorTest, ConstructWithIteratorPair) {
    std::array<custom_type, 5> arr = {
        custom_type(1),
        custom_type(2),
        custom_type(3),
        custom_type(5),
        custom_type(8)
    };

    static_vector<custom_type, SIZE_VECTOR> v(arr.begin(), arr.end());

    EXPECT_EQ(arr.size() * 2, custom_type::num_instances());

    auto get_expected_value = [&arr](std::size_t pos) -> decltype(auto)
        { return custom_type(constructed_with::copy_ctor, arr[pos].get()); };
    assert_static_vector_values(v, arr.size(), get_expected_value);
}

TEST(StaticVectorTest, ConstructWithInitializerList) {
    std::size_t const SIZE = 5;
    static_vector<custom_type, SIZE_VECTOR> v = {
        custom_type(1),
        custom_type(2),
        custom_type(3),
        custom_type(5),
        custom_type(8)
    };

    EXPECT_EQ(SIZE, custom_type::num_instances());

    int expected_values[] = {1, 2, 3, 5, 8};
    auto get_expected_value = [&expected_values](std::size_t pos) -> decltype(auto)
        { return custom_type(constructed_with::copy_ctor, expected_values[pos]); };
    assert_static_vector_values(v, SIZE, get_expected_value);
}

TEST(StaticVectorTest, ConstructWithAnotherVector) {
    std::size_t const SIZE = 5;
    static_vector<custom_type, SIZE_VECTOR> v1 = {
        custom_type(1),
        custom_type(2),
        custom_type(3),
        custom_type(5),
        custom_type(8)
    };

    static_vector<custom_type, SIZE_VECTOR> v2 = v1;

    EXPECT_EQ(SIZE * 2, custom_type::num_instances());

    int expected_values[] = {1, 2, 3, 5, 8};
    auto get_expected_value = [&expected_values](std::size_t pos) -> decltype(auto)
        { return custom_type(constructed_with::copy_ctor, expected_values[pos]); };
    assert_static_vector_values(v2, SIZE, get_expected_value);
}
<|endoftext|>


<commit_before>#ifndef __GLOBALS_HPP_INCLUDED
#define __GLOBALS_HPP_INCLUDED

#ifdef __GNUC__
#define __STRUCT_DEFAULT_VALUES_ARE_ACCEPTED
#elif defined(_WIN32)
#if (_MSC_VER >= 1900)
#define __STRUCT_DEFAULT_VALUES_ARE_ACCEPTED
#endif
#endif

#include <string>
#include <vector>
#include <stdint.h> // uint32_t etc.

// Include GLEW
#ifndef __GL_GLEW_H_INCLUDED
#define __GL_GLEW_H_INCLUDED
#include <GL/glew.h>
#endif

// Include GLFW
#ifndef __GLFW3_H_INCLUDED
#define __GLFW3_H_INCLUDED
#include <glfw3.h>
#endif

// Include GLM
#ifndef __GLM_GLM_HPP_INCLUDED
#define __GLM_GLM_HPP_INCLUDED
#include <glm/glm.hpp>
#endif

#ifndef WINDOW_WIDTH
#define WINDOW_WIDTH (1600.0f)
#endif

#ifndef WINDOW_HEIGHT
#define WINDOW_HEIGHT (900.0f)
#endif

#ifndef ASPECT_RATIO
#define ASPECT_RATIO (WINDOW_WIDTH / WINDOW_HEIGHT)
#endif

#ifndef PI
#define PI 3.14159265359f
#endif

#ifndef EARTH_RADIUS
#define EARTH_RADIUS 6371000.0f
#endif

#ifndef TEXT_SIZE
#define TEXT_SIZE 40
#endif

#ifndef FONT_SIZE
#define FONT_SIZE 16
#endif

#ifndef MAX_FPS
#define MAX_FPS 60
#endif

extern glm::vec3 position;
extern GLfloat gravity;
extern bool inFlightmode;
extern GLfloat fallSpeed;
extern double horizontalAngle;
extern double verticalAngle;
extern GLfloat initialFoV;
extern double earth_radius;
extern bool hasMouseEverMoved;
extern bool is_invert_mouse_in_use;
extern bool is_key_I_released;
extern bool is_world_loaded;                 // no more than one world object can be loaded. TODO: check that no more than one world is loaded!
extern bool is_world_spherical;

typedef struct
{
#ifdef __STRUCT_DEFAULT_VALUES_ARE_ACCEPTED
    void *world_pointer = NULL;              // pointer to the world (draw list).
#else
    void *world_pointer;                     // pointer to the world (draw list).
#endif
    std::string vertex_shader;               // filename of vertex shader.
    std::string fragment_shader;             // filename of fragment shader.
} ShaderStruct;

#ifdef __STRUCT_DEFAULT_VALUES_ARE_ACCEPTED
#define SHADERSTRUCT(x) \
ShaderStruct x
#else
#define SHADERSTRUCT(x) \
ShaderStruct x; x.world_pointer = NULL
#endif

typedef struct
{
#ifdef __STRUCT_DEFAULT_VALUES_ARE_ACCEPTED
    void *shader_pointer = NULL;             // pointer to the shader.
#else
    void *shader_pointer;                    // pointer to the shader.
#endif
    std::string texture_file_format;         // type of the texture file. supported file formats so far: `"bmp"`/`"BMP"`, `"dds"`/`"DDS"`.
    std::string texture_filename;            // filename of the model file.
    std::string image_path;
} TextureStruct;

#ifdef __STRUCT_DEFAULT_VALUES_ARE_ACCEPTED
#define TEXTURESTRUCT(x) \
TextureStruct x
#else
#define TEXTURESTRUCT(x) \
TextureStruct x; x.shader_pointer = NULL
#endif

typedef struct
{
    GLuint nodeID;
#ifdef __STRUCT_DEFAULT_VALUES_ARE_ACCEPTED
    void *graph_pointer = NULL;
#else
    void *graph_pointer;
#endif
    glm::vec3 coordinate_vector;
    std::vector<uint32_t> neighbor_nodeIDs;
} NodeStruct;

#ifdef __STRUCT_DEFAULT_VALUES_ARE_ACCEPTED
#define NODESTRUCT(x) \
NodeStruct x
#else
#define NODESTRUCT(x) \
NodeStruct x; x.graph_pointer = NULL
#endif

typedef struct
{
#ifdef __STRUCT_DEFAULT_VALUES_ARE_ACCEPTED
    void *species_pointer = NULL;    // pointer to the species.
    GLfloat rotate_angle = NAN;      // rotate angle.
#else
    void *species_pointer;           // pointer to the species.
    GLfloat rotate_angle;            // rotate angle.
#endif
    glm::vec3 coordinate_vector;     // coordinate vector.
    glm::vec3 rotate_vector;         // rotate vector.
    glm::vec3 translate_vector;      // translate vector.
    glm::mat4 model_matrix;          // model matrix.
    glm::mat4 MVP_matrix;            // model view projection matrix.
} ObjectStruct;

#ifdef __STRUCT_DEFAULT_VALUES_ARE_ACCEPTED
#define OBJECTSTRUCT(x) \
ObjectStruct x
#else
#define OBJECTSTRUCT(x) \
ObjectStruct x; x.species_pointer = NULL; x.rotate_angle = NAN
#endif

typedef struct
{
    // used for all files (for all species).
#ifdef __STRUCT_DEFAULT_VALUES_ARE_ACCEPTED
    void *texture_pointer = NULL;            // pointer to the texture object.
    bool is_world = false;                   // worlds currently do not rotate nor translate.
    double world_radius = NAN;               // radius of sea level in meters. used only for worlds.
#else
    void *texture_pointer;                   // pointer to the texture object.
    bool is_world;                           // worlds currently do not rotate nor translate.
    double world_radius;                     // radius of sea level in meters. used only for worlds.
#endif
    std::string model_file_format;           // type of the model file. supported file formats so far: `"bmp"`/`"BMP"`, `"obj"`/`"OBJ"`.
                                             // TODO: add support for `"SRTM"`.
    std::string vertex_shader;               // filename of vertex shader.
    std::string fragment_shader;             // filename of fragment shader.
    glm::vec3 lightPos;                      // light position.
    std::vector<ObjectStruct> object_vector; // vector of individual objects of this species.
    std::string coordinate_system;           // used only for worlds (`is_world` == `true`). valid values: `"cartesian"`.
                                             // TODO: add support for `"spherical"`. `"spherical"` is used eg. in SRTM heightmaps.

    // for `"bmp"` model files.
    std::string model_filename;              // filename of the model file.
    std::string color_channel;               // color channel to use for altitude data.
} SpeciesStruct;

#ifdef __STRUCT_DEFAULT_VALUES_ARE_ACCEPTED
#define SPECIESSTRUCT(x) \
SpeciesStruct x
#else
#define SPECIESSTRUCT(x) \
SpeciesStruct x; x.texture_pointer = NULL; x.is_world = false; x.world_radius = NAN
#endif

typedef struct
{
    GLuint screen_width;
    GLuint screen_height;
    GLuint x;
    GLuint y;
    GLuint text_size;
    GLuint font_size;
    const char *text;
    const char *char_font_texture_file_format;
    const char *horizontal_alignment;
    const char *vertical_alignment;
} PrintingStruct;

typedef struct
{
    double rho;
    double theta;
    double phi;
} SphericalCoordinatesStruct;

typedef struct
{
    double southern_latitude;
    double northern_latitude;
    double western_longitude;
    double eastern_longitude;
} SphericalWorldStruct;

extern SphericalCoordinatesStruct spherical_position;

#endif
<commit_msg>Nyt myös Clangilla (Clang 3.7.0) kääntäminen onnistuu.<commit_after>#ifndef __GLOBALS_HPP_INCLUDED
#define __GLOBALS_HPP_INCLUDED

#ifdef __clang__
#elif defined(__GNUC__)
#define __STRUCT_DEFAULT_VALUES_ARE_ACCEPTED
#elif defined(_WIN32)
#if (_MSC_VER >= 1900)
#define __STRUCT_DEFAULT_VALUES_ARE_ACCEPTED
#endif
#endif

#include <string>
#include <vector>
#include <stdint.h> // uint32_t etc.

// Include GLEW
#ifndef __GL_GLEW_H_INCLUDED
#define __GL_GLEW_H_INCLUDED
#include <GL/glew.h>
#endif

// Include GLFW
#ifndef __GLFW3_H_INCLUDED
#define __GLFW3_H_INCLUDED
#include <glfw3.h>
#endif

// Include GLM
#ifndef __GLM_GLM_HPP_INCLUDED
#define __GLM_GLM_HPP_INCLUDED
#include <glm/glm.hpp>
#endif

#ifndef WINDOW_WIDTH
#define WINDOW_WIDTH (1600.0f)
#endif

#ifndef WINDOW_HEIGHT
#define WINDOW_HEIGHT (900.0f)
#endif

#ifndef ASPECT_RATIO
#define ASPECT_RATIO (WINDOW_WIDTH / WINDOW_HEIGHT)
#endif

#ifndef PI
#define PI 3.14159265359f
#endif

#ifndef EARTH_RADIUS
#define EARTH_RADIUS 6371000.0f
#endif

#ifndef TEXT_SIZE
#define TEXT_SIZE 40
#endif

#ifndef FONT_SIZE
#define FONT_SIZE 16
#endif

#ifndef MAX_FPS
#define MAX_FPS 60
#endif

extern glm::vec3 position;
extern GLfloat gravity;
extern bool inFlightmode;
extern GLfloat fallSpeed;
extern double horizontalAngle;
extern double verticalAngle;
extern GLfloat initialFoV;
extern double earth_radius;
extern bool hasMouseEverMoved;
extern bool is_invert_mouse_in_use;
extern bool is_key_I_released;
extern bool is_world_loaded;                 // no more than one world object can be loaded. TODO: check that no more than one world is loaded!
extern bool is_world_spherical;

typedef struct
{
#ifdef __STRUCT_DEFAULT_VALUES_ARE_ACCEPTED
    void *world_pointer = NULL;              // pointer to the world (draw list).
#else
    void *world_pointer;                     // pointer to the world (draw list).
#endif
    std::string vertex_shader;               // filename of vertex shader.
    std::string fragment_shader;             // filename of fragment shader.
} ShaderStruct;

#ifdef __STRUCT_DEFAULT_VALUES_ARE_ACCEPTED
#define SHADERSTRUCT(x) \
ShaderStruct x
#else
#define SHADERSTRUCT(x) \
ShaderStruct x; x.world_pointer = NULL
#endif

typedef struct
{
#ifdef __STRUCT_DEFAULT_VALUES_ARE_ACCEPTED
    void *shader_pointer = NULL;             // pointer to the shader.
#else
    void *shader_pointer;                    // pointer to the shader.
#endif
    std::string texture_file_format;         // type of the texture file. supported file formats so far: `"bmp"`/`"BMP"`, `"dds"`/`"DDS"`.
    std::string texture_filename;            // filename of the model file.
    std::string image_path;
} TextureStruct;

#ifdef __STRUCT_DEFAULT_VALUES_ARE_ACCEPTED
#define TEXTURESTRUCT(x) \
TextureStruct x
#else
#define TEXTURESTRUCT(x) \
TextureStruct x; x.shader_pointer = NULL
#endif

typedef struct
{
    GLuint nodeID;
#ifdef __STRUCT_DEFAULT_VALUES_ARE_ACCEPTED
    void *graph_pointer = NULL;
#else
    void *graph_pointer;
#endif
    glm::vec3 coordinate_vector;
    std::vector<uint32_t> neighbor_nodeIDs;
} NodeStruct;

#ifdef __STRUCT_DEFAULT_VALUES_ARE_ACCEPTED
#define NODESTRUCT(x) \
NodeStruct x
#else
#define NODESTRUCT(x) \
NodeStruct x; x.graph_pointer = NULL
#endif

typedef struct
{
#ifdef __STRUCT_DEFAULT_VALUES_ARE_ACCEPTED
    void *species_pointer = NULL;    // pointer to the species.
    GLfloat rotate_angle = NAN;      // rotate angle.
#else
    void *species_pointer;           // pointer to the species.
    GLfloat rotate_angle;            // rotate angle.
#endif
    glm::vec3 coordinate_vector;     // coordinate vector.
    glm::vec3 rotate_vector;         // rotate vector.
    glm::vec3 translate_vector;      // translate vector.
    glm::mat4 model_matrix;          // model matrix.
    glm::mat4 MVP_matrix;            // model view projection matrix.
} ObjectStruct;

#ifdef __STRUCT_DEFAULT_VALUES_ARE_ACCEPTED
#define OBJECTSTRUCT(x) \
ObjectStruct x
#else
#define OBJECTSTRUCT(x) \
ObjectStruct x; x.species_pointer = NULL; x.rotate_angle = NAN
#endif

typedef struct
{
    // used for all files (for all species).
#ifdef __STRUCT_DEFAULT_VALUES_ARE_ACCEPTED
    void *texture_pointer = NULL;            // pointer to the texture object.
    bool is_world = false;                   // worlds currently do not rotate nor translate.
    double world_radius = NAN;               // radius of sea level in meters. used only for worlds.
#else
    void *texture_pointer;                   // pointer to the texture object.
    bool is_world;                           // worlds currently do not rotate nor translate.
    double world_radius;                     // radius of sea level in meters. used only for worlds.
#endif
    std::string model_file_format;           // type of the model file. supported file formats so far: `"bmp"`/`"BMP"`, `"obj"`/`"OBJ"`.
                                             // TODO: add support for `"SRTM"`.
    std::string vertex_shader;               // filename of vertex shader.
    std::string fragment_shader;             // filename of fragment shader.
    glm::vec3 lightPos;                      // light position.
    std::vector<ObjectStruct> object_vector; // vector of individual objects of this species.
    std::string coordinate_system;           // used only for worlds (`is_world` == `true`). valid values: `"cartesian"`.
                                             // TODO: add support for `"spherical"`. `"spherical"` is used eg. in SRTM heightmaps.

    // for `"bmp"` model files.
    std::string model_filename;              // filename of the model file.
    std::string color_channel;               // color channel to use for altitude data.
} SpeciesStruct;

#ifdef __STRUCT_DEFAULT_VALUES_ARE_ACCEPTED
#define SPECIESSTRUCT(x) \
SpeciesStruct x
#else
#define SPECIESSTRUCT(x) \
SpeciesStruct x; x.texture_pointer = NULL; x.is_world = false; x.world_radius = NAN
#endif

typedef struct
{
    GLuint screen_width;
    GLuint screen_height;
    GLuint x;
    GLuint y;
    GLuint text_size;
    GLuint font_size;
    const char *text;
    const char *char_font_texture_file_format;
    const char *horizontal_alignment;
    const char *vertical_alignment;
} PrintingStruct;

typedef struct
{
    double rho;
    double theta;
    double phi;
} SphericalCoordinatesStruct;

typedef struct
{
    double southern_latitude;
    double northern_latitude;
    double western_longitude;
    double eastern_longitude;
} SphericalWorldStruct;

extern SphericalCoordinatesStruct spherical_position;

#endif
<|endoftext|>


<commit_before>/*

   nsjail
   -----------------------------------------

   Copyright 2014 Google Inc. All Rights Reserved.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

*/

#include "nsjail.h"

#include <fcntl.h>
#include <poll.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <termios.h>
#include <unistd.h>

#include <algorithm>
#include <cerrno>
#include <memory>
#include <vector>

#include "cmdline.h"
#include "logs.h"
#include "macros.h"
#include "net.h"
#include "sandbox.h"
#include "subproc.h"
#include "util.h"

namespace nsjail {

static __thread int sigFatal = 0;
static __thread bool showProc = false;

static void sigHandler(int sig) {
	if (sig == SIGALRM || sig == SIGCHLD || sig == SIGPIPE) {
		return;
	}
	if (sig == SIGUSR1 || sig == SIGQUIT) {
		showProc = true;
		return;
	}
	sigFatal = sig;
}

static bool setSigHandler(int sig) {
	LOG_D("Setting sighandler for signal %s (%d)", util::sigName(sig).c_str(), sig);

	sigset_t smask;
	sigemptyset(&smask);

	struct sigaction sa;
	sa.sa_handler = sigHandler;
	sa.sa_mask = smask;
	sa.sa_flags = 0;
	sa.sa_restorer = NULL;

	if (sig == SIGTTIN || sig == SIGTTOU) {
		sa.sa_handler = SIG_IGN;
	}
	if (sigaction(sig, &sa, NULL) == -1) {
		PLOG_E("sigaction(%d)", sig);
		return false;
	}
	return true;
}

static bool setSigHandlers(void) {
	for (const auto& i : nssigs) {
		if (!setSigHandler(i)) {
			return false;
		}
	}
	return true;
}

static bool setTimer(nsjconf_t* nsjconf) {
	if (nsjconf->mode == MODE_STANDALONE_EXECVE) {
		return true;
	}

	struct itimerval it = {
	    .it_interval =
		{
		    .tv_sec = 1,
		    .tv_usec = 0,
		},
	    .it_value =
		{
		    .tv_sec = 1,
		    .tv_usec = 0,
		},
	};
	if (setitimer(ITIMER_REAL, &it, NULL) == -1) {
		PLOG_E("setitimer(ITIMER_REAL)");
		return false;
	}
	return true;
}

static bool pipeTraffic(nsjconf_t* nsjconf, int listenfd) {
	std::vector<struct pollfd> fds;
	fds.reserve(nsjconf->pipes.size() * 3 + 1);
	for (const auto& p : nsjconf->pipes) {
		fds.push_back({
		    .fd = p.sock_fd,
		    .events = POLLIN | POLLOUT,
		    .revents = 0,
		});
		fds.push_back({
		    .fd = p.pipe_in,
		    .events = POLLOUT,
		    .revents = 0,
		});
		fds.push_back({
		    .fd = p.pipe_out,
		    .events = POLLIN,
		    .revents = 0,
		});
	}
	fds.push_back({
	    .fd = listenfd,
	    .events = POLLIN,
	    .revents = 0,
	});
	LOG_D("Waiting for fd activity");
	while (poll(fds.data(), fds.size(), -1) > 0) {
		if (sigFatal > 0 || showProc) {
			return false;
		}
		if (fds.back().revents != 0) {
			LOG_D("New connection ready");
			return true;
		}
		bool cleanup = false;
		for (size_t i = 0; i < fds.size() - 1; ++i) {
			if (fds[i].revents & POLLIN) {
				fds[i].events &= ~POLLIN;
			}
			if (fds[i].revents & POLLOUT) {
				fds[i].events &= ~POLLOUT;
			}
		}
		for (size_t i = 0; i < fds.size() - 3; i += 3) {
			const size_t pipe_no = i / 3;
			int in, out;
			const char* direction;
			bool closed = false;
			std::tuple<int, int, const char*> direction_map[] = {
			    std::make_tuple(i, i + 1, "in"),
			    std::make_tuple(i + 2, i, "out"),
			};
			for (const auto& entry : direction_map) {
				std::tie(in, out, direction) = entry;
				bool in_ready = (fds[in].events & POLLIN) == 0 ||
						(fds[in].revents & POLLIN) == POLLIN;
				bool out_ready = (fds[out].events & POLLOUT) == 0 ||
						 (fds[out].revents & POLLOUT) == POLLOUT;
				if (in_ready && out_ready) {
					LOG_D("#%zu piping data %s", pipe_no, direction);
					ssize_t rv = splice(fds[in].fd, nullptr, fds[out].fd,
					    nullptr, 4096, SPLICE_F_NONBLOCK);
					if (rv == -1 && errno != EAGAIN) {
						PLOG_E("splice fd pair #%zu {%d, %d}\n", pipe_no,
						    fds[in].fd, fds[out].fd);
					}
					if (rv == 0) {
						closed = true;
					}
					fds[in].events |= POLLIN;
					fds[out].events |= POLLOUT;
				}
				if ((fds[in].revents & (POLLERR | POLLHUP)) != 0 ||
				    (fds[out].revents & (POLLERR | POLLHUP)) != 0) {
					closed = true;
				}
			}
			if (closed) {
				LOG_D("#%zu connection closed", pipe_no);
				cleanup = true;
				close(nsjconf->pipes[pipe_no].sock_fd);
				close(nsjconf->pipes[pipe_no].pipe_in);
				close(nsjconf->pipes[pipe_no].pipe_out);
				if (nsjconf->pipes[pipe_no].pid > 0) {
					kill(nsjconf->pipes[pipe_no].pid, SIGKILL);
				}
				nsjconf->pipes[pipe_no] = {};
			}
		}
		if (cleanup) {
			break;
		}
	}
	nsjconf->pipes.erase(std::remove(nsjconf->pipes.begin(), nsjconf->pipes.end(), pipemap_t{}),
	    nsjconf->pipes.end());
	return false;
}

static int listenMode(nsjconf_t* nsjconf) {
	int listenfd = net::getRecvSocket(nsjconf->bindhost.c_str(), nsjconf->port);
	if (listenfd == -1) {
		return EXIT_FAILURE;
	}
	for (;;) {
		if (sigFatal > 0) {
			subproc::killAndReapAll(
			    nsjconf, nsjconf->forward_signals ? sigFatal : SIGKILL);
			logs::logStop(sigFatal);
			close(listenfd);
			return EXIT_SUCCESS;
		}
		if (showProc) {
			showProc = false;
			subproc::displayProc(nsjconf);
		}
		if (pipeTraffic(nsjconf, listenfd)) {
			int connfd = net::acceptConn(listenfd);
			if (connfd >= 0) {
				int in[2];
				int out[2];
				if (pipe(in) != 0 || pipe(out) != 0) {
					PLOG_E("pipe");
					continue;
				}

				pid_t pid =
				    subproc::runChild(nsjconf, connfd, in[0], out[1], out[1]);

				close(in[0]);
				close(out[1]);

				if (pid <= 0) {
					close(in[1]);
					close(out[0]);
					close(connfd);
				} else {
					nsjconf->pipes.push_back({
					    .sock_fd = connfd,
					    .pipe_in = in[1],
					    .pipe_out = out[0],
					    .pid = pid,
					});
				}
			}
		}
		subproc::reapProc(nsjconf);
	}
}

static int standaloneMode(nsjconf_t* nsjconf) {
	for (;;) {
		if (subproc::runChild(nsjconf, /* netfd= */ -1, STDIN_FILENO, STDOUT_FILENO,
			STDERR_FILENO) == -1) {
			LOG_E("Couldn't launch the child process");
			return 0xff;
		}
		for (;;) {
			int child_status = subproc::reapProc(nsjconf);
			if (subproc::countProc(nsjconf) == 0) {
				if (nsjconf->mode == MODE_STANDALONE_ONCE) {
					return child_status;
				}
				break;
			}
			if (showProc) {
				showProc = false;
				subproc::displayProc(nsjconf);
			}
			if (sigFatal > 0) {
				subproc::killAndReapAll(
				    nsjconf, nsjconf->forward_signals ? sigFatal : SIGKILL);
				logs::logStop(sigFatal);
				return (128 + sigFatal);
			}
			pause();
		}
	}
	// not reached
}

std::unique_ptr<struct termios> getTC(int fd) {
	std::unique_ptr<struct termios> trm(new struct termios);

	if (ioctl(fd, TCGETS, trm.get()) == -1) {
		PLOG_D("ioctl(fd=%d, TCGETS) failed", fd);
		return nullptr;
	}
	LOG_D("Saved the current state of the TTY");
	return trm;
}

void setTC(int fd, const struct termios* trm) {
	if (!trm) {
		return;
	}
	if (ioctl(fd, TCSETS, trm) == -1) {
		PLOG_W("ioctl(fd=%d, TCSETS) failed", fd);
		return;
	}
	if (tcflush(fd, TCIFLUSH) == -1) {
		PLOG_W("tcflush(fd=%d, TCIFLUSH) failed", fd);
		return;
	}
}

}  // namespace nsjail

int main(int argc, char* argv[]) {
	std::unique_ptr<nsjconf_t> nsjconf = cmdline::parseArgs(argc, argv);
	std::unique_ptr<struct termios> trm = nsjail::getTC(STDIN_FILENO);

	if (!nsjconf) {
		LOG_F("Couldn't parse cmdline options");
	}
	if (nsjconf->daemonize && (daemon(/* nochdir= */ 1, /* noclose= */ 0) == -1)) {
		PLOG_F("daemon");
	}
	cmdline::logParams(nsjconf.get());
	if (!nsjail::setSigHandlers()) {
		LOG_F("nsjail::setSigHandlers() failed");
	}
	if (!nsjail::setTimer(nsjconf.get())) {
		LOG_F("nsjail::setTimer() failed");
	}
	if (!sandbox::preparePolicy(nsjconf.get())) {
		LOG_F("Couldn't prepare sandboxing policy");
	}

	int ret = 0;
	if (nsjconf->mode == MODE_LISTEN_TCP) {
		ret = nsjail::listenMode(nsjconf.get());
	} else {
		ret = nsjail::standaloneMode(nsjconf.get());
	}

	sandbox::closePolicy(nsjconf.get());
	/* Try to restore the underlying console's params in case some program has changed it */
	if (!nsjconf->daemonize) {
		nsjail::setTC(STDIN_FILENO, trm.get());
	}

	LOG_D("Returning with %d", ret);
	return ret;
}
<commit_msg>nsjail: use atomic in sighandlers<commit_after>/*

   nsjail
   -----------------------------------------

   Copyright 2014 Google Inc. All Rights Reserved.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

*/

#include "nsjail.h"

#include <fcntl.h>
#include <poll.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <termios.h>
#include <unistd.h>

#include <algorithm>
#include <atomic>
#include <cerrno>
#include <memory>
#include <vector>

#include "cmdline.h"
#include "logs.h"
#include "macros.h"
#include "net.h"
#include "sandbox.h"
#include "subproc.h"
#include "util.h"

namespace nsjail {

static __thread std::atomic<int> sigFatal(0);
static __thread std::atomic<bool> showProc(false);

static void sigHandler(int sig) {
	if (sig == SIGALRM || sig == SIGCHLD || sig == SIGPIPE) {
		return;
	}
	if (sig == SIGUSR1 || sig == SIGQUIT) {
		showProc = true;
		return;
	}
	sigFatal = sig;
}

static bool setSigHandler(int sig) {
	LOG_D("Setting sighandler for signal %s (%d)", util::sigName(sig).c_str(), sig);

	sigset_t smask;
	sigemptyset(&smask);

	struct sigaction sa;
	sa.sa_handler = sigHandler;
	sa.sa_mask = smask;
	sa.sa_flags = 0;
	sa.sa_restorer = NULL;

	if (sig == SIGTTIN || sig == SIGTTOU) {
		sa.sa_handler = SIG_IGN;
	}
	if (sigaction(sig, &sa, NULL) == -1) {
		PLOG_E("sigaction(%d)", sig);
		return false;
	}
	return true;
}

static bool setSigHandlers(void) {
	for (const auto& i : nssigs) {
		if (!setSigHandler(i)) {
			return false;
		}
	}
	return true;
}

static bool setTimer(nsjconf_t* nsjconf) {
	if (nsjconf->mode == MODE_STANDALONE_EXECVE) {
		return true;
	}

	struct itimerval it = {
	    .it_interval =
		{
		    .tv_sec = 1,
		    .tv_usec = 0,
		},
	    .it_value =
		{
		    .tv_sec = 1,
		    .tv_usec = 0,
		},
	};
	if (setitimer(ITIMER_REAL, &it, NULL) == -1) {
		PLOG_E("setitimer(ITIMER_REAL)");
		return false;
	}
	return true;
}

static bool pipeTraffic(nsjconf_t* nsjconf, int listenfd) {
	std::vector<struct pollfd> fds;
	fds.reserve(nsjconf->pipes.size() * 3 + 1);
	for (const auto& p : nsjconf->pipes) {
		fds.push_back({
		    .fd = p.sock_fd,
		    .events = POLLIN | POLLOUT,
		    .revents = 0,
		});
		fds.push_back({
		    .fd = p.pipe_in,
		    .events = POLLOUT,
		    .revents = 0,
		});
		fds.push_back({
		    .fd = p.pipe_out,
		    .events = POLLIN,
		    .revents = 0,
		});
	}
	fds.push_back({
	    .fd = listenfd,
	    .events = POLLIN,
	    .revents = 0,
	});
	LOG_D("Waiting for fd activity");
	while (poll(fds.data(), fds.size(), -1) > 0) {
		if (sigFatal > 0 || showProc) {
			return false;
		}
		if (fds.back().revents != 0) {
			LOG_D("New connection ready");
			return true;
		}
		bool cleanup = false;
		for (size_t i = 0; i < fds.size() - 1; ++i) {
			if (fds[i].revents & POLLIN) {
				fds[i].events &= ~POLLIN;
			}
			if (fds[i].revents & POLLOUT) {
				fds[i].events &= ~POLLOUT;
			}
		}
		for (size_t i = 0; i < fds.size() - 3; i += 3) {
			const size_t pipe_no = i / 3;
			int in, out;
			const char* direction;
			bool closed = false;
			std::tuple<int, int, const char*> direction_map[] = {
			    std::make_tuple(i, i + 1, "in"),
			    std::make_tuple(i + 2, i, "out"),
			};
			for (const auto& entry : direction_map) {
				std::tie(in, out, direction) = entry;
				bool in_ready = (fds[in].events & POLLIN) == 0 ||
						(fds[in].revents & POLLIN) == POLLIN;
				bool out_ready = (fds[out].events & POLLOUT) == 0 ||
						 (fds[out].revents & POLLOUT) == POLLOUT;
				if (in_ready && out_ready) {
					LOG_D("#%zu piping data %s", pipe_no, direction);
					ssize_t rv = splice(fds[in].fd, nullptr, fds[out].fd,
					    nullptr, 4096, SPLICE_F_NONBLOCK);
					if (rv == -1 && errno != EAGAIN) {
						PLOG_E("splice fd pair #%zu {%d, %d}\n", pipe_no,
						    fds[in].fd, fds[out].fd);
					}
					if (rv == 0) {
						closed = true;
					}
					fds[in].events |= POLLIN;
					fds[out].events |= POLLOUT;
				}
				if ((fds[in].revents & (POLLERR | POLLHUP)) != 0 ||
				    (fds[out].revents & (POLLERR | POLLHUP)) != 0) {
					closed = true;
				}
			}
			if (closed) {
				LOG_D("#%zu connection closed", pipe_no);
				cleanup = true;
				close(nsjconf->pipes[pipe_no].sock_fd);
				close(nsjconf->pipes[pipe_no].pipe_in);
				close(nsjconf->pipes[pipe_no].pipe_out);
				if (nsjconf->pipes[pipe_no].pid > 0) {
					kill(nsjconf->pipes[pipe_no].pid, SIGKILL);
				}
				nsjconf->pipes[pipe_no] = {};
			}
		}
		if (cleanup) {
			break;
		}
	}
	nsjconf->pipes.erase(std::remove(nsjconf->pipes.begin(), nsjconf->pipes.end(), pipemap_t{}),
	    nsjconf->pipes.end());
	return false;
}

static int listenMode(nsjconf_t* nsjconf) {
	int listenfd = net::getRecvSocket(nsjconf->bindhost.c_str(), nsjconf->port);
	if (listenfd == -1) {
		return EXIT_FAILURE;
	}
	for (;;) {
		if (sigFatal > 0) {
			subproc::killAndReapAll(
			    nsjconf, nsjconf->forward_signals ? sigFatal.load() : SIGKILL);
			logs::logStop(sigFatal);
			close(listenfd);
			return EXIT_SUCCESS;
		}
		if (showProc) {
			showProc = false;
			subproc::displayProc(nsjconf);
		}
		if (pipeTraffic(nsjconf, listenfd)) {
			int connfd = net::acceptConn(listenfd);
			if (connfd >= 0) {
				int in[2];
				int out[2];
				if (pipe(in) != 0 || pipe(out) != 0) {
					PLOG_E("pipe");
					continue;
				}

				pid_t pid =
				    subproc::runChild(nsjconf, connfd, in[0], out[1], out[1]);

				close(in[0]);
				close(out[1]);

				if (pid <= 0) {
					close(in[1]);
					close(out[0]);
					close(connfd);
				} else {
					nsjconf->pipes.push_back({
					    .sock_fd = connfd,
					    .pipe_in = in[1],
					    .pipe_out = out[0],
					    .pid = pid,
					});
				}
			}
		}
		subproc::reapProc(nsjconf);
	}
}

static int standaloneMode(nsjconf_t* nsjconf) {
	for (;;) {
		if (subproc::runChild(nsjconf, /* netfd= */ -1, STDIN_FILENO, STDOUT_FILENO,
			STDERR_FILENO) == -1) {
			LOG_E("Couldn't launch the child process");
			return 0xff;
		}
		for (;;) {
			int child_status = subproc::reapProc(nsjconf);
			if (subproc::countProc(nsjconf) == 0) {
				if (nsjconf->mode == MODE_STANDALONE_ONCE) {
					return child_status;
				}
				break;
			}
			if (showProc) {
				showProc = false;
				subproc::displayProc(nsjconf);
			}
			if (sigFatal > 0) {
				subproc::killAndReapAll(
				    nsjconf, nsjconf->forward_signals ? sigFatal.load() : SIGKILL);
				logs::logStop(sigFatal);
				return (128 + sigFatal);
			}
			pause();
		}
	}
	// not reached
}

std::unique_ptr<struct termios> getTC(int fd) {
	std::unique_ptr<struct termios> trm(new struct termios);

	if (ioctl(fd, TCGETS, trm.get()) == -1) {
		PLOG_D("ioctl(fd=%d, TCGETS) failed", fd);
		return nullptr;
	}
	LOG_D("Saved the current state of the TTY");
	return trm;
}

void setTC(int fd, const struct termios* trm) {
	if (!trm) {
		return;
	}
	if (ioctl(fd, TCSETS, trm) == -1) {
		PLOG_W("ioctl(fd=%d, TCSETS) failed", fd);
		return;
	}
	if (tcflush(fd, TCIFLUSH) == -1) {
		PLOG_W("tcflush(fd=%d, TCIFLUSH) failed", fd);
		return;
	}
}

}  // namespace nsjail

int main(int argc, char* argv[]) {
	std::unique_ptr<nsjconf_t> nsjconf = cmdline::parseArgs(argc, argv);
	std::unique_ptr<struct termios> trm = nsjail::getTC(STDIN_FILENO);

	if (!nsjconf) {
		LOG_F("Couldn't parse cmdline options");
	}
	if (nsjconf->daemonize && (daemon(/* nochdir= */ 1, /* noclose= */ 0) == -1)) {
		PLOG_F("daemon");
	}
	cmdline::logParams(nsjconf.get());
	if (!nsjail::setSigHandlers()) {
		LOG_F("nsjail::setSigHandlers() failed");
	}
	if (!nsjail::setTimer(nsjconf.get())) {
		LOG_F("nsjail::setTimer() failed");
	}
	if (!sandbox::preparePolicy(nsjconf.get())) {
		LOG_F("Couldn't prepare sandboxing policy");
	}

	int ret = 0;
	if (nsjconf->mode == MODE_LISTEN_TCP) {
		ret = nsjail::listenMode(nsjconf.get());
	} else {
		ret = nsjail::standaloneMode(nsjconf.get());
	}

	sandbox::closePolicy(nsjconf.get());
	/* Try to restore the underlying console's params in case some program has changed it */
	if (!nsjconf->daemonize) {
		nsjail::setTC(STDIN_FILENO, trm.get());
	}

	LOG_D("Returning with %d", ret);
	return ret;
}
<|endoftext|>