<commit_before>/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*-
 *
 * Quadra, an action puzzle game
 * Copyright (C) 1998-2000  Ludus Design
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

const char *keynames[256] = {
  "", "Escape", "1", "2", "3", "4", "5", "6",
  "7", "8", "9", "0", "-", "=", "Backspace", "Tab",
  "Q", "W", "E", "R", "T", "Y", "U", "I",
  "O", "P", "[", "]", "Enter", "Ctrl", "A", "S",
  "D", "F", "G", "H", "J", "K", "L", ";",
  "'", "`", "Left shift", "\\", "Z", "X", "C", "V",
  "B", "N", "M", ",", ".", "/", "Right shift", "Pad *",
  "Alt", "Space", "Caps lock", "F1", "F2", "F3", "F4", "F5",
  "F6", "F7", "F8", "F9", "F10", "Num lock", "Scrl lock", "Pad 7",
  "Pad 8", "Pad 9", "Pad -", "Pad 4", "Pad 5", "Pad 6", "Pad +",
  "Pad 1", "Pad 2", "Pad 3", "Pad 0", "Pad .", "Print scrn", "",
  "<", "F11", "F12", "", "", "", "", "", "", "","Pad Enter",
  "Right Ctrl", "Pad /", "PrintScrn", "Alt Char", "Pause",
  "Home", "Up", "Page Up", "Left", "Right", "End", "Down",
  "Page Down", "Insert", "Delete",
  "", "", "", "", "", "", "", "Pause",
  "", "", "", "", "", "Win left", "Win right", "Win popup",
  "", "Pause", "", "", "", "", "", "",
  "", "", "", "", "", "", "", "",
  "", "", "", "", "", "", "", "",
  "", "", "", "", "Pad Enter", "2nd Ctrl", "", "",
  "", "", "", "", "", "", "", "",
  "", "", "", "", "", "", "", "",
  "", "", "", "", "", "Pad /", "", "",
  "2nd Alt", "", "", "", "", "", "", "",
  "", "", "", "", "", "", "", "Home",
  "Up", "Page up", "", "Left", "", "Right", "", "End",
  "Down", "Page down", "Insert", "Del", "", "", "", "",
  "", "", "", "Win left", "Win right", "Win popup", "", "",
  "", "", "", "", "", "", "", "",
  "", "", "", "", "", "", "", "Macro",
  "", "", "", "", "", "", "", "",
  "", "", "", "", "", "", "", ""
};

#ifdef UGS_LINUX_SVGA

RCSID("$Id$")

#include "wraplib.h"
#include "video.h"
#include "input_svga.h"

static Svgalib* lib;

Input_Svgalib::Input_Svgalib() {
  struct sigaction newsignals;

  lib = getSvgalib();

  tty_fd = 0;

  pause = false;

  clear_key();

  mouse.dx = mouse.dy = mouse.dz = 0;
  restore_mouse();

  tcgetattr(0, &termattr);

  israw = false;
  reraw();

  newsignals.sa_handler = Input_Svgalib::signal_handler;
  sigemptyset(&newsignals.sa_mask);
  newsignals.sa_flags = 0;
  newsignals.sa_restorer = NULL;
  sigaction(SIGUSR2, &newsignals, &oldsignals);
}

Input_Svgalib::~Input_Svgalib() {
  sigaction(SIGUSR2, &oldsignals, NULL);
  lib->mouse_close();
  deraw();

  tcsetattr(0, TCSANOW, &termattr);
}

void Input_Svgalib::clear_key() {
  shift_key = 0;
  quel_key = -1;
  key_pending = 0;
  for(int i=0; i<256; i++)
    keys[i] = 0;
  if(israw)
    lib->keyboard_clearstate();
}

void Input_Svgalib::check() {
  if(video) {
    if(mouse_reinit) {
      video->restore();
      restore_mouse();
    }
  }

  process_key();
  process_mouse();
}

void Input_Svgalib::deraw() {
  if(israw) {
    lib->keyboard_close();
    israw=false;
  }
}

void Input_Svgalib::reraw() {
  if(!israw) {
    lib->keyboard_init();
    lib->keyboard_seteventhandler(Input_Svgalib::keyboard_handler);
    lib->keyboard_translatekeys(8 /* DONT_CATCH_CTRLC */);
    israw=true;
  }
}

void Input_Svgalib::process_key() {
  int thekey;

  if(israw)
    lib->keyboard_update();
  else {
    fflush(stdin);
    thekey = lib->vga_getkey();
    if(thekey) {
      switch (thekey) {
      case 27:
	quel_key = KEY_ESCAPE;
	break;
      case 13:
      case 10:
	quel_key = KEY_ENTER;
	break;
      default:
	if(key_pending < MAXKEY) {
	  key_buf[key_pending].c = thekey;
	  key_buf[key_pending].special = false;
	  key_pending++;
	}
      }
    }
  }
}

void Input_Svgalib::process_mouse() {
  mouse.dx = mouse.dy = mouse.dz = 0;
  lib->mouse_update();
}

void Input_Svgalib::restore_mouse() {
  mouse_reinit = false;
  lib->mouse_seteventhandler((void*) Input_Svgalib::mouse_handler);
  mouse.quel = -1;
  for(int i=0; i<4; i++)
    mouse.button[i] = RELEASED;
}

void Input_Svgalib::mouse_handler(int button,
				  int dx, int dy, int dz,
				  int drx, int dry, int drz) {
  input->mouse.dx += dx;
  input->mouse.dy += dy;
  input->mouse.dz += dz;

  if(input->mouse.button[0] == RELEASED && button & 4)
    input->mouse.quel = 0;
  if(input->mouse.button[1] == RELEASED && button & 2)
    input->mouse.quel = 1;
  if(input->mouse.button[2] == RELEASED && button & 1)
    input->mouse.quel = 2;

  input->mouse.button[0] = (Byte) ((button & 4) ? PRESSED:RELEASED);
  input->mouse.button[1] = (Byte) ((button & 2) ? PRESSED:RELEASED);
  input->mouse.button[2] = (Byte) ((button & 1) ? PRESSED:RELEASED);
}

void Input_Svgalib::keyboard_handler(int scancode, int press) {
  if(press) {
    input->keys[scancode] |= PRESSED;
    switch(scancode) {
    case KEY_RSHIFT:
    case KEY_LSHIFT:
      input->shift_key |= SHIFT;
      break;
    case KEY_RALT:
    case KEY_LALT:
      input->shift_key |= ALT;
      break;
    case KEY_RCTRL:
    case KEY_LCTRL:
      input->shift_key |= CONTROL;
      break;
    case 101:
    case 119:
      input->pause = true;
      break;
    default:
      input->quel_key = scancode;
    }
  } else {
    input->keys[scancode] = RELEASED;
    switch(scancode) {
    case KEY_RSHIFT:
    case KEY_LSHIFT:
      input->shift_key &= ~SHIFT;
      break;
    case KEY_RALT:
    case KEY_LALT:
      input->shift_key &= ~ALT;
      break;
    case KEY_RCTRL:
    case KEY_LCTRL:
      input->shift_key &= ~CONTROL;
      break;
    }
  }
}

void Input_Svgalib::signal_handler(int signal) {
  /* indique que la souris devra etre re-initer au prochain refresh */
  ((Input_Svgalib*)input)->mouse_reinit = true;
	((Input_Svgalib*)input)->oldsignals.sa_handler(signal);
}

#endif /* UGS_LINUX_SVGA */

<commit_msg>Using a macro before including the file that uses it is not good, fixed it.<commit_after>/* -*- Mode: C++; c-basic-offset: 2; tab-width: 2; indent-tabs-mode: nil -*-
 *
 * Quadra, an action puzzle game
 * Copyright (C) 1998-2000  Ludus Design
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

const char *keynames[256] = {
  "", "Escape", "1", "2", "3", "4", "5", "6",
  "7", "8", "9", "0", "-", "=", "Backspace", "Tab",
  "Q", "W", "E", "R", "T", "Y", "U", "I",
  "O", "P", "[", "]", "Enter", "Ctrl", "A", "S",
  "D", "F", "G", "H", "J", "K", "L", ";",
  "'", "`", "Left shift", "\\", "Z", "X", "C", "V",
  "B", "N", "M", ",", ".", "/", "Right shift", "Pad *",
  "Alt", "Space", "Caps lock", "F1", "F2", "F3", "F4", "F5",
  "F6", "F7", "F8", "F9", "F10", "Num lock", "Scrl lock", "Pad 7",
  "Pad 8", "Pad 9", "Pad -", "Pad 4", "Pad 5", "Pad 6", "Pad +",
  "Pad 1", "Pad 2", "Pad 3", "Pad 0", "Pad .", "Print scrn", "",
  "<", "F11", "F12", "", "", "", "", "", "", "","Pad Enter",
  "Right Ctrl", "Pad /", "PrintScrn", "Alt Char", "Pause",
  "Home", "Up", "Page Up", "Left", "Right", "End", "Down",
  "Page Down", "Insert", "Delete",
  "", "", "", "", "", "", "", "Pause",
  "", "", "", "", "", "Win left", "Win right", "Win popup",
  "", "Pause", "", "", "", "", "", "",
  "", "", "", "", "", "", "", "",
  "", "", "", "", "", "", "", "",
  "", "", "", "", "Pad Enter", "2nd Ctrl", "", "",
  "", "", "", "", "", "", "", "",
  "", "", "", "", "", "", "", "",
  "", "", "", "", "", "Pad /", "", "",
  "2nd Alt", "", "", "", "", "", "", "",
  "", "", "", "", "", "", "", "Home",
  "Up", "Page up", "", "Left", "", "Right", "", "End",
  "Down", "Page down", "Insert", "Del", "", "", "", "",
  "", "", "", "Win left", "Win right", "Win popup", "", "",
  "", "", "", "", "", "", "", "",
  "", "", "", "", "", "", "", "Macro",
  "", "", "", "", "", "", "", "",
  "", "", "", "", "", "", "", ""
};

#ifdef UGS_LINUX_SVGA

#include "wraplib.h"
#include "video.h"
#include "input_svga.h"

RCSID("$Id$")

static Svgalib* lib;

Input_Svgalib::Input_Svgalib() {
  struct sigaction newsignals;

  lib = getSvgalib();

  tty_fd = 0;

  pause = false;

  clear_key();

  mouse.dx = mouse.dy = mouse.dz = 0;
  restore_mouse();

  tcgetattr(0, &termattr);

  israw = false;
  reraw();

  newsignals.sa_handler = Input_Svgalib::signal_handler;
  sigemptyset(&newsignals.sa_mask);
  newsignals.sa_flags = 0;
  newsignals.sa_restorer = NULL;
  sigaction(SIGUSR2, &newsignals, &oldsignals);
}

Input_Svgalib::~Input_Svgalib() {
  sigaction(SIGUSR2, &oldsignals, NULL);
  lib->mouse_close();
  deraw();

  tcsetattr(0, TCSANOW, &termattr);
}

void Input_Svgalib::clear_key() {
  shift_key = 0;
  quel_key = -1;
  key_pending = 0;
  for(int i=0; i<256; i++)
    keys[i] = 0;
  if(israw)
    lib->keyboard_clearstate();
}

void Input_Svgalib::check() {
  if(video) {
    if(mouse_reinit) {
      video->restore();
      restore_mouse();
    }
  }

  process_key();
  process_mouse();
}

void Input_Svgalib::deraw() {
  if(israw) {
    lib->keyboard_close();
    israw=false;
  }
}

void Input_Svgalib::reraw() {
  if(!israw) {
    lib->keyboard_init();
    lib->keyboard_seteventhandler(Input_Svgalib::keyboard_handler);
    lib->keyboard_translatekeys(8 /* DONT_CATCH_CTRLC */);
    israw=true;
  }
}

void Input_Svgalib::process_key() {
  int thekey;

  if(israw)
    lib->keyboard_update();
  else {
    fflush(stdin);
    thekey = lib->vga_getkey();
    if(thekey) {
      switch (thekey) {
      case 27:
	quel_key = KEY_ESCAPE;
	break;
      case 13:
      case 10:
	quel_key = KEY_ENTER;
	break;
      default:
	if(key_pending < MAXKEY) {
	  key_buf[key_pending].c = thekey;
	  key_buf[key_pending].special = false;
	  key_pending++;
	}
      }
    }
  }
}

void Input_Svgalib::process_mouse() {
  mouse.dx = mouse.dy = mouse.dz = 0;
  lib->mouse_update();
}

void Input_Svgalib::restore_mouse() {
  mouse_reinit = false;
  lib->mouse_seteventhandler((void*) Input_Svgalib::mouse_handler);
  mouse.quel = -1;
  for(int i=0; i<4; i++)
    mouse.button[i] = RELEASED;
}

void Input_Svgalib::mouse_handler(int button,
				  int dx, int dy, int dz,
				  int drx, int dry, int drz) {
  input->mouse.dx += dx;
  input->mouse.dy += dy;
  input->mouse.dz += dz;

  if(input->mouse.button[0] == RELEASED && button & 4)
    input->mouse.quel = 0;
  if(input->mouse.button[1] == RELEASED && button & 2)
    input->mouse.quel = 1;
  if(input->mouse.button[2] == RELEASED && button & 1)
    input->mouse.quel = 2;

  input->mouse.button[0] = (Byte) ((button & 4) ? PRESSED:RELEASED);
  input->mouse.button[1] = (Byte) ((button & 2) ? PRESSED:RELEASED);
  input->mouse.button[2] = (Byte) ((button & 1) ? PRESSED:RELEASED);
}

void Input_Svgalib::keyboard_handler(int scancode, int press) {
  if(press) {
    input->keys[scancode] |= PRESSED;
    switch(scancode) {
    case KEY_RSHIFT:
    case KEY_LSHIFT:
      input->shift_key |= SHIFT;
      break;
    case KEY_RALT:
    case KEY_LALT:
      input->shift_key |= ALT;
      break;
    case KEY_RCTRL:
    case KEY_LCTRL:
      input->shift_key |= CONTROL;
      break;
    case 101:
    case 119:
      input->pause = true;
      break;
    default:
      input->quel_key = scancode;
    }
  } else {
    input->keys[scancode] = RELEASED;
    switch(scancode) {
    case KEY_RSHIFT:
    case KEY_LSHIFT:
      input->shift_key &= ~SHIFT;
      break;
    case KEY_RALT:
    case KEY_LALT:
      input->shift_key &= ~ALT;
      break;
    case KEY_RCTRL:
    case KEY_LCTRL:
      input->shift_key &= ~CONTROL;
      break;
    }
  }
}

void Input_Svgalib::signal_handler(int signal) {
  /* indique que la souris devra etre re-initer au prochain refresh */
  ((Input_Svgalib*)input)->mouse_reinit = true;
	((Input_Svgalib*)input)->oldsignals.sa_handler(signal);
}

#endif /* UGS_LINUX_SVGA */

<|endoftext|>


<commit_before>/*$Id$
 *
 * This source file is a part of the Fresco Project.
 * Copyright (C) 2000 Stefan Seefeld <stefan@fresco.org>
 * http://www.fresco.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
 * MA 02139, USA.
 */

#include <Prague/Sys/Tracer.hh>
#include <Prague/Sys/SHM.hh>
#include <Fresco/config.hh>
#include <Fresco/Transform.hh>
#include <Fresco/DrawTraversal.hh>
#include <Fresco/DrawingKit.hh>
#include <Berlin/Logger.hh>
#include "VisualImpl.hh"
#include <sys/ipc.h>
#include <strstream.h>

using namespace Prague;
using namespace Fresco;

GGIDrawableFactory *VisualImpl::_factory = 0;

VisualImpl::VisualImpl(PixelCoord w, PixelCoord h)
  : ControllerImpl(false), _width(w), _height(h)
{
  Trace trace("VisualImpl::VisualImpl");
  Console *console = Console::instance();
  if (!_factory) _factory = console->get_extension<GGIDrawableFactory>("GGIDrawableFactory");
  Fresco::Drawable::PixelFormat format = Console::instance()->drawable()->pixel_format();
  /*
   * the drawable plus some memory for the event queue
   */
  size_t size = w * h * format.size + 64*1024;
  _shm = SHM::allocate(size);
  std::ostrstream oss;

  _ggi = _factory->create_drawable(_shm, w, h, 3);
  _drawable = console->activate_drawable(_ggi);
  ggi_mode mode = _ggi->mode();
  char buffer[256];
  ggiSPrintMode(buffer, &mode);
  _mode = buffer;
}

VisualImpl::~VisualImpl()
{
  Trace trace("VisualImpl::~VisualImpl");
  SHM::deallocate(_shm);
}

char *VisualImpl::name()
{
  std::cout << _ggi->name() << std::endl;
  return CORBA::string_dup(_ggi->name().c_str());
}

char *VisualImpl::mode()
{
  return CORBA::string_dup(_mode.c_str());
}

void VisualImpl::request(Fresco::Graphic::Requisition &requisition)
{
  Trace trace("VisualImpl::request");
  requisition.x.defined = true;
  requisition.x.natural = requisition.x.maximum = requisition.x.minimum = _width * 10;// / _drawable->resolution(xaxis);
  requisition.x.align = 0.;
  requisition.y.defined = true;
  requisition.y.natural = requisition.y.maximum = requisition.y.minimum = _height * 10;// / _drawable->resolution(yaxis);
  requisition.y.align = 0.;
  requisition.z.defined = false;
}

void VisualImpl::draw(DrawTraversal_ptr traversal)
{
  Trace trace("VisualImpl::draw");
  DrawingKit_var drawing = traversal->drawing();
  drawing->copy_drawable(_drawable, 0, 0, _width, _height);
}

void VisualImpl::extension(const Allocation::Info &info, Region_ptr region)
{
  Trace trace("VisualImpl::extension");
  GraphicImpl::extension(info, region);
}

CORBA::Boolean VisualImpl::handle_positional(PickTraversal_ptr traversal, const Fresco::Input::Event &event)
{
  Trace trace("VisualImpl::handle_positional");
  ControllerImpl::handle_positional(traversal, event);
}

CORBA::Boolean VisualImpl::handle_non_positional(const Fresco::Input::Event &event)
{
  Trace trace("VisualImpl::handle_non_positional");
  std::cout << "VisualImpl::handle_non_positional" << std::endl;
  /*
   * FIXME !: we assume a lot about the (berlin) event layout here. Make that more flexible...
   */
  Input::Toggle toggle = event[0].attr.selection();
  ggi_event ggi;
  // giiEventSend will discard events without this field filled in
  // I'm not sure if the actual value should depend on the source of the
  // event, or what (there's ggi.any.source for that, but that's filled in
  // as being a fake event by EventSend; this seems to be about which of the
  // input queues are handed the event.
  ggi.any.target = GII_EV_TARGET_ALL;
  if (toggle.actuation == Input::Toggle::press) ggi.any.type = evKeyPress;
  else if (toggle.actuation == Input::Toggle::hold) ggi.any.type = evKeyRepeat;
  else if (toggle.actuation == Input::Toggle::release) ggi.any.type = evKeyRelease; // not generated actually
  ggi.key.sym = toggle.number;
  /*
   * FIXME !: the ggi_event structure is quite incomplete here. The real structure looks so:

   typedef struct {

   COMMON_DATA;

   uint32	modifiers;	current modifiers in effect
   uint32	sym;		meaning of key
   uint32  label;		label on key
   uint32  button;		button number

   } gii_key_event;

   * and we need to figure out a way to fill the remaining members, i.e. reconstruct them from the sym
   * given that this is the only information we conserve in the berlin key event...
   */
  forward_event(ggi);
}

void VisualImpl::forward_event(const ggi_event &event)
{
  Trace trace("VisualImpl::forward_event");
//  giiEventSend(ggiJoinInputs(_ggi->visual(), 0), const_cast<ggi_event *>(&event));
  ggiEventSend(_ggi->visual(), const_cast<ggi_event *>(&event));
}
<commit_msg>Removed unused strstream<commit_after>/*$Id$
 *
 * This source file is a part of the Fresco Project.
 * Copyright (C) 2000 Stefan Seefeld <stefan@fresco.org>
 * http://www.fresco.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 675 Mass Ave, Cambridge,
 * MA 02139, USA.
 */

#include <Prague/Sys/Tracer.hh>
#include <Prague/Sys/SHM.hh>
#include <Fresco/config.hh>
#include <Fresco/Transform.hh>
#include <Fresco/DrawTraversal.hh>
#include <Fresco/DrawingKit.hh>
#include <Berlin/Logger.hh>
#include "VisualImpl.hh"
#include <sys/ipc.h>

using namespace Prague;
using namespace Fresco;

GGIDrawableFactory *VisualImpl::_factory = 0;

VisualImpl::VisualImpl(PixelCoord w, PixelCoord h)
  : ControllerImpl(false), _width(w), _height(h)
{
  Trace trace("VisualImpl::VisualImpl");
  Console *console = Console::instance();
  if (!_factory) _factory = console->get_extension<GGIDrawableFactory>("GGIDrawableFactory");
  Fresco::Drawable::PixelFormat format = Console::instance()->drawable()->pixel_format();
  /*
   * the drawable plus some memory for the event queue
   */
  size_t size = w * h * format.size + 64*1024;
  _shm = SHM::allocate(size);

  _ggi = _factory->create_drawable(_shm, w, h, 3);
  _drawable = console->activate_drawable(_ggi);
  ggi_mode mode = _ggi->mode();
  char buffer[256];
  ggiSPrintMode(buffer, &mode);
  _mode = buffer;
}

VisualImpl::~VisualImpl()
{
  Trace trace("VisualImpl::~VisualImpl");
  SHM::deallocate(_shm);
}

char *VisualImpl::name()
{
  std::cout << _ggi->name() << std::endl;
  return CORBA::string_dup(_ggi->name().c_str());
}

char *VisualImpl::mode()
{
  return CORBA::string_dup(_mode.c_str());
}

void VisualImpl::request(Fresco::Graphic::Requisition &requisition)
{
  Trace trace("VisualImpl::request");
  requisition.x.defined = true;
  requisition.x.natural = requisition.x.maximum = requisition.x.minimum = _width * 10;// / _drawable->resolution(xaxis);
  requisition.x.align = 0.;
  requisition.y.defined = true;
  requisition.y.natural = requisition.y.maximum = requisition.y.minimum = _height * 10;// / _drawable->resolution(yaxis);
  requisition.y.align = 0.;
  requisition.z.defined = false;
}

void VisualImpl::draw(DrawTraversal_ptr traversal)
{
  Trace trace("VisualImpl::draw");
  DrawingKit_var drawing = traversal->drawing();
  drawing->copy_drawable(_drawable, 0, 0, _width, _height);
}

void VisualImpl::extension(const Allocation::Info &info, Region_ptr region)
{
  Trace trace("VisualImpl::extension");
  GraphicImpl::extension(info, region);
}

CORBA::Boolean VisualImpl::handle_positional(PickTraversal_ptr traversal, const Fresco::Input::Event &event)
{
  Trace trace("VisualImpl::handle_positional");
  ControllerImpl::handle_positional(traversal, event);
}

CORBA::Boolean VisualImpl::handle_non_positional(const Fresco::Input::Event &event)
{
  Trace trace("VisualImpl::handle_non_positional");
  std::cout << "VisualImpl::handle_non_positional" << std::endl;
  /*
   * FIXME !: we assume a lot about the (berlin) event layout here. Make that more flexible...
   */
  Input::Toggle toggle = event[0].attr.selection();
  ggi_event ggi;
  // giiEventSend will discard events without this field filled in
  // I'm not sure if the actual value should depend on the source of the
  // event, or what (there's ggi.any.source for that, but that's filled in
  // as being a fake event by EventSend; this seems to be about which of the
  // input queues are handed the event.
  ggi.any.target = GII_EV_TARGET_ALL;
  if (toggle.actuation == Input::Toggle::press) ggi.any.type = evKeyPress;
  else if (toggle.actuation == Input::Toggle::hold) ggi.any.type = evKeyRepeat;
  else if (toggle.actuation == Input::Toggle::release) ggi.any.type = evKeyRelease; // not generated actually
  ggi.key.sym = toggle.number;
  /*
   * FIXME !: the ggi_event structure is quite incomplete here. The real structure looks so:

   typedef struct {

   COMMON_DATA;

   uint32	modifiers;	current modifiers in effect
   uint32	sym;		meaning of key
   uint32  label;		label on key
   uint32  button;		button number

   } gii_key_event;

   * and we need to figure out a way to fill the remaining members, i.e. reconstruct them from the sym
   * given that this is the only information we conserve in the berlin key event...
   */
  forward_event(ggi);
}

void VisualImpl::forward_event(const ggi_event &event)
{
  Trace trace("VisualImpl::forward_event");
//  giiEventSend(ggiJoinInputs(_ggi->visual(), 0), const_cast<ggi_event *>(&event));
  ggiEventSend(_ggi->visual(), const_cast<ggi_event *>(&event));
}
<|endoftext|>


<commit_before>#ifndef _VKREF_HPP
#define _VKREF_HPP
/*-------------------------------------------------------------------------
 * Vulkan CTS Framework
 * --------------------
 *
 * Copyright (c) 2015 Google Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and/or associated documentation files (the
 * "Materials"), to deal in the Materials without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Materials, and to
 * permit persons to whom the Materials are furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice(s) and this permission notice shall be
 * included in all copies or substantial portions of the Materials.
 *
 * The Materials are Confidential Information as defined by the
 * Khronos Membership Agreement until designated non-confidential by
 * Khronos, at which point this condition clause shall be removed.
 *
 * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
 *
 *//*!
 * \file
 * \brief Vulkan object reference holder.
 *//*--------------------------------------------------------------------*/

#include "vkDefs.hpp"
#include "vkStrUtil.hpp"
#include "deUniquePtr.hpp"
#include "deMeta.hpp"

#include <algorithm>

namespace vk
{

namespace refdetails
{

using std::swap;

template<typename T>
struct Checked
{
	explicit inline		Checked		(T object_) : object(object_) {}

	T					object;
};

//! Check that object is not null
template<typename T>
inline Checked<T> check (T object)
{
	if (!object)
		throw tcu::TestError("Object check() failed", (std::string(getTypeName<T>()) + " = 0").c_str(), __FILE__, __LINE__);
	return Checked<T>(object);
}

//! Declare object as checked earlier
template<typename T>
inline Checked<T> notNull (T object)
{
	if (!object)
		throw tcu::InternalError("Null object was given to notNull()", (std::string(getTypeName<T>()) + " = 0").c_str(), __FILE__, __LINE__);
	return Checked<T>(object);
}

//! Allow null object
template<typename T>
inline Checked<T> allowNull (T object)
{
	return Checked<T>(object);
}

template<typename T>
class Deleter
{
public:
							Deleter		(const DeviceInterface& deviceIface, VkDevice device)
								: m_deviceIface	(&deviceIface)
								, m_device		(device)
							{}
							Deleter		(void)
								: m_deviceIface	(DE_NULL)
								, m_device		(DE_NULL)
							{}

	void					operator()	(T obj) const;

private:
	const DeviceInterface*	m_deviceIface;
	VkDevice				m_device;
};

template<>
class Deleter<VkInstance>
{
public:
							Deleter		(const PlatformInterface& platformIface, VkInstance instance)
								: m_destroyInstance((DestroyInstanceFunc)platformIface.getInstanceProcAddr(instance, "vkDestroyInstance"))
							{}
							Deleter		(void)
								: m_destroyInstance((DestroyInstanceFunc)DE_NULL)
							{}

	void					operator()	(VkInstance obj) const { DE_TEST_ASSERT(m_destroyInstance(obj) == VK_SUCCESS); }

private:
	DestroyInstanceFunc		m_destroyInstance;
};

template<>
class Deleter<VkDevice>
{
public:
							Deleter		(const InstanceInterface& instanceIface, VkDevice device)
								: m_destroyDevice((DestroyDeviceFunc)instanceIface.getDeviceProcAddr(device, "vkDestroyDevice"))
							{}
							Deleter		(void)
								: m_destroyDevice((DestroyDeviceFunc)DE_NULL)
							{}

	void					operator()	(VkDevice obj) const { DE_TEST_ASSERT(m_destroyDevice(obj) == VK_SUCCESS); }

private:
	DestroyDeviceFunc		m_destroyDevice;
};

template<typename T>
struct RefData
{
				RefData		(T object_, Deleter<T> deleter_)
								: object	(object_)
								, deleter	(deleter_)
				{}
				RefData		(void)
								: object	(0)
				{}

	T			object;
	Deleter<T>	deleter;
};

template<typename T>
class RefBase
{
public:
						~RefBase	(void);

	inline const T&		get			(void) const throw() { return m_data.object;	}
	inline const T&		operator*	(void) const throw() { return get();			}
	inline operator		bool		(void) const throw() { return !!get();			}

protected:
						RefBase		(RefData<T> data) : m_data(data)	{}

	void				reset		(void);				//!< Release previous object, set to null.
	RefData<T>			disown		(void) throw();		//!< Disown and return object (ownership transferred to caller).
	void				assign		(RefData<T> data);	//!< Set new pointer, release previous pointer.

private:
	RefData<T>			m_data;
};

template<typename T>
inline RefBase<T>::~RefBase (void)
{
	this->reset();
}

template<typename T>
inline void RefBase<T>::reset (void)
{
	if (!!m_data.object)
		m_data.deleter(m_data.object);

	m_data = RefData<T>();
}

template<typename T>
inline RefData<T> RefBase<T>::disown (void) throw()
{
	RefData<T> tmp;
	swap(m_data, tmp);
	return tmp;
}

template<typename T>
inline void RefBase<T>::assign (RefData<T> data)
{
	this->reset();
	m_data = data;
}

/*--------------------------------------------------------------------*//*!
 * \brief Movable Vulkan object reference.
 *
 * Similar to de::MovePtr.
 *//*--------------------------------------------------------------------*/
template<typename T>
class Move : public RefBase<T>
{
public:
	template<typename U>
				Move		(Checked<U> object, Deleter<U> deleter)
								: RefBase<T>(RefData<T>(object.object, deleter))
				{}

				Move		(RefData<T> data)
								: RefBase<T>(data)
				{}
				Move		(void)
								: RefBase<T>(RefData<T>())
				{}

	Move<T>&	operator=	(Move<T>& other);
	Move<T>&	operator=	(RefData<T> data);

	operator	RefData<T>	(void) { return this->disown(); }
};

template<typename T>
inline Move<T>& Move<T>::operator= (Move<T>& other)
{
	if (this != &other)
		this->assign(other.disown());

	return *this;
}

template<typename T>
inline Move<T>& Move<T>::operator= (RefData<T> data)
{
	this->assign(data);
	return *this;
}

/*--------------------------------------------------------------------*//*!
 * \brief Unique Vulkan object reference.
 *
 * Similar to de::UniquePtr.
 *//*--------------------------------------------------------------------*/
template<typename T>
class Unique : public RefBase<T>
{
public:
	template<typename U>
				Unique		(Checked<U> object, Deleter<U> deleter)
								: RefBase<T>(RefData<T>(object.object, deleter))
				{}

				Unique		(RefData<T> data)
								: RefBase<T>(data)
				{}
};

} // refdetails

using refdetails::Move;
using refdetails::Unique;
using refdetails::Deleter;
using refdetails::check;
using refdetails::notNull;
using refdetails::allowNull;

#include "vkRefUtil.inl"

Move<VkPipeline>	createGraphicsPipeline	(const DeviceInterface& vk, VkDevice device, VkPipelineCache pipelineCache, const VkGraphicsPipelineCreateInfo* pCreateInfo);
Move<VkPipeline>	createComputePipeline	(const DeviceInterface& vk, VkDevice device, VkPipelineCache pipelineCache, const VkComputePipelineCreateInfo* pCreateInfo);

} // vk

#endif // _VKREF_HPP
<commit_msg>Fixes to vk::Move<> and vk::Unique<><commit_after>#ifndef _VKREF_HPP
#define _VKREF_HPP
/*-------------------------------------------------------------------------
 * Vulkan CTS Framework
 * --------------------
 *
 * Copyright (c) 2015 Google Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and/or associated documentation files (the
 * "Materials"), to deal in the Materials without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Materials, and to
 * permit persons to whom the Materials are furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice(s) and this permission notice shall be
 * included in all copies or substantial portions of the Materials.
 *
 * The Materials are Confidential Information as defined by the
 * Khronos Membership Agreement until designated non-confidential by
 * Khronos, at which point this condition clause shall be removed.
 *
 * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
 *
 *//*!
 * \file
 * \brief Vulkan object reference holder.
 *//*--------------------------------------------------------------------*/

#include "vkDefs.hpp"
#include "vkStrUtil.hpp"
#include "deUniquePtr.hpp"
#include "deMeta.hpp"

#include <algorithm>

namespace vk
{

namespace refdetails
{

using std::swap;

template<typename T>
struct Checked
{
	explicit inline		Checked		(T object_) : object(object_) {}

	T					object;
};

//! Check that object is not null
template<typename T>
inline Checked<T> check (T object)
{
	if (!object)
		throw tcu::TestError("Object check() failed", (std::string(getTypeName<T>()) + " = 0").c_str(), __FILE__, __LINE__);
	return Checked<T>(object);
}

//! Declare object as checked earlier
template<typename T>
inline Checked<T> notNull (T object)
{
	if (!object)
		throw tcu::InternalError("Null object was given to notNull()", (std::string(getTypeName<T>()) + " = 0").c_str(), __FILE__, __LINE__);
	return Checked<T>(object);
}

//! Allow null object
template<typename T>
inline Checked<T> allowNull (T object)
{
	return Checked<T>(object);
}

template<typename T>
class Deleter
{
public:
							Deleter		(const DeviceInterface& deviceIface, VkDevice device)
								: m_deviceIface	(&deviceIface)
								, m_device		(device)
							{}
							Deleter		(void)
								: m_deviceIface	(DE_NULL)
								, m_device		(DE_NULL)
							{}

	void					operator()	(T obj) const;

private:
	const DeviceInterface*	m_deviceIface;
	VkDevice				m_device;
};

template<>
class Deleter<VkInstance>
{
public:
							Deleter		(const PlatformInterface& platformIface, VkInstance instance)
								: m_destroyInstance((DestroyInstanceFunc)platformIface.getInstanceProcAddr(instance, "vkDestroyInstance"))
							{}
							Deleter		(void)
								: m_destroyInstance((DestroyInstanceFunc)DE_NULL)
							{}

	void					operator()	(VkInstance obj) const { DE_TEST_ASSERT(m_destroyInstance(obj) == VK_SUCCESS); }

private:
	DestroyInstanceFunc		m_destroyInstance;
};

template<>
class Deleter<VkDevice>
{
public:
							Deleter		(const InstanceInterface& instanceIface, VkDevice device)
								: m_destroyDevice((DestroyDeviceFunc)instanceIface.getDeviceProcAddr(device, "vkDestroyDevice"))
							{}
							Deleter		(void)
								: m_destroyDevice((DestroyDeviceFunc)DE_NULL)
							{}

	void					operator()	(VkDevice obj) const { DE_TEST_ASSERT(m_destroyDevice(obj) == VK_SUCCESS); }

private:
	DestroyDeviceFunc		m_destroyDevice;
};

template<typename T>
struct RefData
{
				RefData		(T object_, Deleter<T> deleter_)
								: object	(object_)
								, deleter	(deleter_)
				{}
				RefData		(void)
								: object	(0)
				{}

	T			object;
	Deleter<T>	deleter;
};

template<typename T>
class RefBase
{
public:
						~RefBase	(void);

	inline const T&		get			(void) const throw() { return m_data.object;	}
	inline const T&		operator*	(void) const throw() { return get();			}
	inline operator		bool		(void) const throw() { return !!get();			}

protected:
						RefBase		(RefData<T> data) : m_data(data)	{}

	void				reset		(void);				//!< Release previous object, set to null.
	RefData<T>			disown		(void) throw();		//!< Disown and return object (ownership transferred to caller).
	void				assign		(RefData<T> data);	//!< Set new pointer, release previous pointer.

private:
	RefData<T>			m_data;
};

template<typename T>
inline RefBase<T>::~RefBase (void)
{
	this->reset();
}

template<typename T>
inline void RefBase<T>::reset (void)
{
	if (!!m_data.object)
		m_data.deleter(m_data.object);

	m_data = RefData<T>();
}

template<typename T>
inline RefData<T> RefBase<T>::disown (void) throw()
{
	RefData<T> tmp;
	swap(m_data, tmp);
	return tmp;
}

template<typename T>
inline void RefBase<T>::assign (RefData<T> data)
{
	this->reset();
	m_data = data;
}

/*--------------------------------------------------------------------*//*!
 * \brief Movable Vulkan object reference.
 *
 * Similar to de::MovePtr.
 *//*--------------------------------------------------------------------*/
template<typename T>
class Move : public RefBase<T>
{
public:
	template<typename U>
				Move		(Checked<U> object, Deleter<U> deleter)
								: RefBase<T>(RefData<T>(object.object, deleter))
				{}

				Move		(RefData<T> data)
								: RefBase<T>(data)
				{}
				Move		(Move<T>& other)
								: RefBase<T>(other.disown())
				{}
				Move		(void)
								: RefBase<T>(RefData<T>())
				{}

	Move<T>&	operator=	(Move<T>& other);
	Move<T>&	operator=	(RefData<T> data);

	operator	RefData<T>	(void) { return this->disown(); }
};

template<typename T>
inline Move<T>& Move<T>::operator= (Move<T>& other)
{
	if (this != &other)
		this->assign(other.disown());

	return *this;
}

template<typename T>
inline Move<T>& Move<T>::operator= (RefData<T> data)
{
	this->assign(data);
	return *this;
}

/*--------------------------------------------------------------------*//*!
 * \brief Unique Vulkan object reference.
 *
 * Similar to de::UniquePtr.
 *//*--------------------------------------------------------------------*/
template<typename T>
class Unique : public RefBase<T>
{
public:
	template<typename U>
				Unique		(Checked<U> object, Deleter<U> deleter)
								: RefBase<T>(RefData<T>(object.object, deleter))
				{}

				Unique		(RefData<T> data)
								: RefBase<T>(data)
				{}

private:
				Unique		(const Unique<T>&);
	Unique<T>&	operator=	(const Unique<T>&);
};

} // refdetails

using refdetails::Move;
using refdetails::Unique;
using refdetails::Deleter;
using refdetails::check;
using refdetails::notNull;
using refdetails::allowNull;

#include "vkRefUtil.inl"

Move<VkPipeline>	createGraphicsPipeline	(const DeviceInterface& vk, VkDevice device, VkPipelineCache pipelineCache, const VkGraphicsPipelineCreateInfo* pCreateInfo);
Move<VkPipeline>	createComputePipeline	(const DeviceInterface& vk, VkDevice device, VkPipelineCache pipelineCache, const VkComputePipelineCreateInfo* pCreateInfo);

} // vk

#endif // _VKREF_HPP
<|endoftext|>


<commit_before>#include <cthun-agent/agent.hpp>
#include <cthun-agent/errors.hpp>
#include <cthun-agent/uuid.hpp>
#include <cthun-agent/string_utils.hpp>
#include <cthun-agent/external_module.hpp>
#include <cthun-agent/modules/echo.hpp>
#include <cthun-agent/modules/inventory.hpp>
#include <cthun-agent/modules/ping.hpp>
#include <cthun-agent/modules/status.hpp>

#include <cthun-client/connector/errors.hpp>
#include <cthun-client/data_container/data_container.hpp>
#include <cthun-client/validator/validator.hpp>  // Validator

#define LEATHERMAN_LOGGING_NAMESPACE "puppetlabs.cthun_agent.agent"
#include <leatherman/logging/logging.hpp>

#include <vector>
#include <memory>

namespace CthunAgent {

namespace fs = boost::filesystem;

//
// Constants
//

static const std::string AGENT_CLIENT_TYPE { "agent" };
static const std::string CTHUN_REQUEST_SCHEMA_NAME { "http://puppetlabs.com/cnc_request" };
static const std::string CTHUN_RESPONSE_SCHEMA_NAME { "http://puppetlabs.com/cnc_response" };

static const int DEFAULT_MSG_TIMEOUT_SEC { 10 };

//
// Agent
//

Agent::Agent(const std::string& modules_dir,
             const std::string& server_url,
             const std::string& ca,
             const std::string& crt,
             const std::string& key)
        try
            : connector_ { server_url, AGENT_CLIENT_TYPE, ca, crt, key },
              modules_ {} {
    // NB: certificate paths are validated by HW
    loadInternalModules();

    if (!modules_dir.empty()) {
        loadExternalModulesFrom(modules_dir);
    } else {
        LOG_INFO("The modules directory was not provided; no external module "
                 "will be loaded");
    }

    logLoadedModules();
} catch (CthunClient::connection_config_error& e) {
    throw fatal_error { std::string("failed to configure the agent: ")
                        + e.what() };
}

void Agent::start() {
    connector_.registerMessageCallback(
        getCncRequestSchema(),
        [this](const CthunClient::ParsedChunks& parsed_chunks) {
            cncRequestCallback(parsed_chunks);
        });

    try {
        connector_.connect();
    } catch (CthunClient::connection_config_error& e) {
        LOG_ERROR("Failed to configure the underlying communications layer: %1%",
                  e.what());
        throw fatal_error { "failed to configure the underlying communications"
                            "layer" };
    } catch (CthunClient::connection_fatal_error& e) {
        LOG_ERROR("Failed to connect: %1%", e.what());
        throw fatal_error { "failed to connect" };
    }

    // The agent is now connected and the request handlers are set;
    // we can now call the monitoring method that will block this
    // thread of execution.
    // Note that, in case the underlying connection drops, the
    // connector will keep trying to re-establish it indefinitely
    // (the max_connect_attempts is 0 by default).
    try {
        connector_.monitorConnection();
    } catch (CthunClient::connection_fatal_error) {
        throw fatal_error { "failed to reconnect" };
    }

    // TODO(ale): wait for the associate session response, once the
    // associated state is exposed by Connector
}

//
// Agent - private interface
//

void Agent::loadInternalModules() {
    modules_["echo"] = std::shared_ptr<Module>(new Modules::Echo);
    modules_["inventory"] = std::shared_ptr<Module>(new Modules::Inventory);
    modules_["ping"] = std::shared_ptr<Module>(new Modules::Ping);
    modules_["status"] = std::shared_ptr<Module>(new Modules::Status);
}

void Agent::loadExternalModulesFrom(fs::path dir_path) {
    LOG_INFO("Loading external modules from %1%", dir_path.string());

    if (fs::is_directory(dir_path)) {
        fs::directory_iterator end;

        for (auto f = fs::directory_iterator(dir_path); f != end; ++f) {
            if (!fs::is_directory(f->status())) {
                auto f_p = f->path().string();

                try {
                    ExternalModule* e_m = new ExternalModule(f_p);
                    modules_[e_m->module_name] = std::shared_ptr<Module>(e_m);
                } catch (module_error& e) {
                    LOG_ERROR("Failed to load %1%; %2%", f_p, e.what());
                } catch (std::exception& e) {
                    LOG_ERROR("Unexpected error when loading %1%; %2%",
                              f_p, e.what());
                } catch (...) {
                    LOG_ERROR("Unexpected error when loading %1%", f_p);
                }
            }
        }
    } else {
        LOG_WARNING("Failed to locate the modules directory; external modules "
                    "will not be loaded");
    }
}

void Agent::logLoadedModules() const {
    for (auto& module : modules_) {
        std::string txt { "found no action" };
        std::string actions_list { "" };

        for (auto& action : module.second->actions) {
            if (actions_list.empty()) {
                txt = "action";
                actions_list += ": ";
            } else {
                actions_list += ", ";
            }
            actions_list += action.first;
        }

        auto txt_suffix = StringUtils::plural(module.second->actions.size());
        LOG_INFO("Loaded '%1%' module - %2%%3%%4%",
                 module.first, txt, txt_suffix, actions_list);
    }
}

CthunClient::Schema Agent::getCncRequestSchema() const {
    CthunClient::Schema schema { CTHUN_REQUEST_SCHEMA_NAME,
                                 CthunClient::ContentType::Json };
    using T_C = CthunClient::TypeConstraint;
    schema.addConstraint("module", T_C::String, true);
    schema.addConstraint("action", T_C::String, true);
    // TODO(ale): evaluate changing params; ambiguous
    schema.addConstraint("params", T_C::Object, false);
    return schema;
}

void Agent::cncRequestCallback(const CthunClient::ParsedChunks& parsed_chunks) {
    auto request_id = parsed_chunks.envelope.get<std::string>("id");
    auto requester_endpoint = parsed_chunks.envelope.get<std::string>("sender");

    LOG_INFO("Received message %1% from %2%", request_id, requester_endpoint);
    LOG_DEBUG("Message %1%:\n%2%", request_id, parsed_chunks.toString());

    try {
        // Inspect envelope
        if (!parsed_chunks.has_data) {
            throw request_validation_error { "no data" };
        }

        if (parsed_chunks.data_type != CthunClient::ContentType::Json) {
            throw request_validation_error { "data is not in JSON format" };
        }

        // Inspect data
        auto module_name = parsed_chunks.data.get<std::string>("module");
        auto action_name = parsed_chunks.data.get<std::string>("action");

        if (modules_.find(module_name) == modules_.end()) {
            throw request_validation_error { "unknown module: " + module_name };
        }

        // Perform the requested action
        auto module_ptr = modules_.at(module_name);
        auto data_json = module_ptr->performRequest(action_name, parsed_chunks);

        // Wrap debug data
        std::vector<CthunClient::DataContainer> debug {};
        for (auto& debug_entry : parsed_chunks.debug) {
            debug.push_back(debug_entry);
        }

        try {
            connector_.send(std::vector<std::string> { requester_endpoint },
                            CTHUN_RESPONSE_SCHEMA_NAME,
                            DEFAULT_MSG_TIMEOUT_SEC,
                            data_json,
                            debug);
        } catch (CthunClient::connection_error& e) {
            // We failed to send the response; it's up to the
            // requester to ask again
            LOG_ERROR("Failed to reply to the %1% request from %2%: %3%",
                      request_id, requester_endpoint, e.what());
        }
    } catch (request_error& e) {
        LOG_ERROR("Failed to process message %1% from %2%: %3%",
                  request_id, requester_endpoint, e.what());
        CthunClient::DataContainer error_data_json;

        // TODO(ale): make error handling consistent in our protocol
        // specs; we should have an error schema

        // TODO(ale): we should specify the id of the request; more in
        // general, we should specify the request id in every response

        error_data_json.set<std::string>("error", e.what());

        try {
            connector_.send(std::vector<std::string> { requester_endpoint },
                            CTHUN_RESPONSE_SCHEMA_NAME,
                            DEFAULT_MSG_TIMEOUT_SEC,
                            error_data_json);
        } catch (CthunClient::connection_error& e) {
            LOG_ERROR("Failed send an error response to the %1% request "
                      "from %2%: %3%", request_id, requester_endpoint, e.what());
        }
    }
}

}  // namespace CthunAgent
<commit_msg>(CTH-214) Send a cnc error message in case of failure<commit_after>#include <cthun-agent/agent.hpp>
#include <cthun-agent/errors.hpp>
#include <cthun-agent/uuid.hpp>
#include <cthun-agent/string_utils.hpp>
#include <cthun-agent/external_module.hpp>
#include <cthun-agent/modules/echo.hpp>
#include <cthun-agent/modules/inventory.hpp>
#include <cthun-agent/modules/ping.hpp>
#include <cthun-agent/modules/status.hpp>

#include <cthun-client/connector/errors.hpp>
#include <cthun-client/data_container/data_container.hpp>
#include <cthun-client/validator/validator.hpp>  // Validator

#define LEATHERMAN_LOGGING_NAMESPACE "puppetlabs.cthun_agent.agent"
#include <leatherman/logging/logging.hpp>

#include <vector>
#include <memory>

namespace CthunAgent {

namespace fs = boost::filesystem;

//
// Constants
//

static const std::string AGENT_CLIENT_TYPE { "agent" };
static const std::string CTHUN_REQUEST_SCHEMA_NAME { "http://puppetlabs.com/cnc_request" };
static const std::string CTHUN_RESPONSE_SCHEMA_NAME { "http://puppetlabs.com/cnc_response" };
static const std::string CTHUN_ERROR_SCHEMA_NAME { "http://puppetlabs.com/cnc_error_message" };

static const int DEFAULT_MSG_TIMEOUT_SEC { 10 };

//
// Agent
//

Agent::Agent(const std::string& modules_dir,
             const std::string& server_url,
             const std::string& ca,
             const std::string& crt,
             const std::string& key)
        try
            : connector_ { server_url, AGENT_CLIENT_TYPE, ca, crt, key },
              modules_ {} {
    // NB: certificate paths are validated by HW
    loadInternalModules();

    if (!modules_dir.empty()) {
        loadExternalModulesFrom(modules_dir);
    } else {
        LOG_INFO("The modules directory was not provided; no external module "
                 "will be loaded");
    }

    logLoadedModules();
} catch (CthunClient::connection_config_error& e) {
    throw fatal_error { std::string("failed to configure the agent: ")
                        + e.what() };
}

void Agent::start() {
    connector_.registerMessageCallback(
        getCncRequestSchema(),
        [this](const CthunClient::ParsedChunks& parsed_chunks) {
            cncRequestCallback(parsed_chunks);
        });

    try {
        connector_.connect();
    } catch (CthunClient::connection_config_error& e) {
        LOG_ERROR("Failed to configure the underlying communications layer: %1%",
                  e.what());
        throw fatal_error { "failed to configure the underlying communications"
                            "layer" };
    } catch (CthunClient::connection_fatal_error& e) {
        LOG_ERROR("Failed to connect: %1%", e.what());
        throw fatal_error { "failed to connect" };
    }

    // The agent is now connected and the request handlers are set;
    // we can now call the monitoring method that will block this
    // thread of execution.
    // Note that, in case the underlying connection drops, the
    // connector will keep trying to re-establish it indefinitely
    // (the max_connect_attempts is 0 by default).
    try {
        connector_.monitorConnection();
    } catch (CthunClient::connection_fatal_error) {
        throw fatal_error { "failed to reconnect" };
    }

    // TODO(ale): wait for the associate session response, once the
    // associated state is exposed by Connector
}

//
// Agent - private interface
//

void Agent::loadInternalModules() {
    modules_["echo"] = std::shared_ptr<Module>(new Modules::Echo);
    modules_["inventory"] = std::shared_ptr<Module>(new Modules::Inventory);
    modules_["ping"] = std::shared_ptr<Module>(new Modules::Ping);
    modules_["status"] = std::shared_ptr<Module>(new Modules::Status);
}

void Agent::loadExternalModulesFrom(fs::path dir_path) {
    LOG_INFO("Loading external modules from %1%", dir_path.string());

    if (fs::is_directory(dir_path)) {
        fs::directory_iterator end;

        for (auto f = fs::directory_iterator(dir_path); f != end; ++f) {
            if (!fs::is_directory(f->status())) {
                auto f_p = f->path().string();

                try {
                    ExternalModule* e_m = new ExternalModule(f_p);
                    modules_[e_m->module_name] = std::shared_ptr<Module>(e_m);
                } catch (module_error& e) {
                    LOG_ERROR("Failed to load %1%; %2%", f_p, e.what());
                } catch (std::exception& e) {
                    LOG_ERROR("Unexpected error when loading %1%; %2%",
                              f_p, e.what());
                } catch (...) {
                    LOG_ERROR("Unexpected error when loading %1%", f_p);
                }
            }
        }
    } else {
        LOG_WARNING("Failed to locate the modules directory; external modules "
                    "will not be loaded");
    }
}

void Agent::logLoadedModules() const {
    for (auto& module : modules_) {
        std::string txt { "found no action" };
        std::string actions_list { "" };

        for (auto& action : module.second->actions) {
            if (actions_list.empty()) {
                txt = "action";
                actions_list += ": ";
            } else {
                actions_list += ", ";
            }
            actions_list += action.first;
        }

        auto txt_suffix = StringUtils::plural(module.second->actions.size());
        LOG_INFO("Loaded '%1%' module - %2%%3%%4%",
                 module.first, txt, txt_suffix, actions_list);
    }
}

CthunClient::Schema Agent::getCncRequestSchema() const {
    CthunClient::Schema schema { CTHUN_REQUEST_SCHEMA_NAME,
                                 CthunClient::ContentType::Json };
    using T_C = CthunClient::TypeConstraint;
    schema.addConstraint("module", T_C::String, true);
    schema.addConstraint("action", T_C::String, true);
    // TODO(ale): evaluate changing params; ambiguous
    schema.addConstraint("params", T_C::Object, false);
    return schema;
}

void Agent::cncRequestCallback(const CthunClient::ParsedChunks& parsed_chunks) {
    auto request_id = parsed_chunks.envelope.get<std::string>("id");
    auto requester_endpoint = parsed_chunks.envelope.get<std::string>("sender");

    LOG_INFO("Received message %1% from %2%", request_id, requester_endpoint);
    LOG_DEBUG("Message %1%:\n%2%", request_id, parsed_chunks.toString());

    try {
        // Inspect envelope
        if (!parsed_chunks.has_data) {
            throw request_validation_error { "no data" };
        }

        if (parsed_chunks.data_type != CthunClient::ContentType::Json) {
            throw request_validation_error { "data is not in JSON format" };
        }

        // Inspect data
        auto module_name = parsed_chunks.data.get<std::string>("module");
        auto action_name = parsed_chunks.data.get<std::string>("action");

        if (modules_.find(module_name) == modules_.end()) {
            throw request_validation_error { "unknown module: " + module_name };
        }

        // Perform the requested action
        auto module_ptr = modules_.at(module_name);
        auto data_json = module_ptr->performRequest(action_name, parsed_chunks);

        // Wrap debug data
        std::vector<CthunClient::DataContainer> debug {};
        for (auto& debug_entry : parsed_chunks.debug) {
            debug.push_back(debug_entry);
        }

        try {
            connector_.send(std::vector<std::string> { requester_endpoint },
                            CTHUN_RESPONSE_SCHEMA_NAME,
                            DEFAULT_MSG_TIMEOUT_SEC,
                            data_json,
                            debug);
        } catch (CthunClient::connection_error& e) {
            // We failed to send the response; it's up to the
            // requester to ask again
            LOG_ERROR("Failed to reply to the %1% request from %2%: %3%",
                      request_id, requester_endpoint, e.what());
        }
    } catch (request_error& e) {
        LOG_ERROR("Failed to process message %1% from %2%: %3%",
                  request_id, requester_endpoint, e.what());
        CthunClient::DataContainer error_data_json;
        error_data_json.set<std::string>("description", e.what());
        error_data_json.set<std::string>("id", request_id);

        try {
            connector_.send(std::vector<std::string> { requester_endpoint },
                            CTHUN_ERROR_SCHEMA_NAME,
                            DEFAULT_MSG_TIMEOUT_SEC,
                            error_data_json);
            LOG_INFO("Replied to message %1% from %2% with an error message",
                     request_id, requester_endpoint);
        } catch (CthunClient::connection_error& e) {
            LOG_ERROR("Failed send an error response to the %1% request "
                      "from %2%: %3%", request_id, requester_endpoint, e.what());
        }
    }
}

}  // namespace CthunAgent
<|endoftext|>


<commit_before>/*

Copyright (c) 2006, MassaRoddel, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "libtorrent/pch.hpp"

#ifdef _MSC_VER
#pragma warning(push, 1)
#endif

#include <boost/shared_ptr.hpp>

#ifdef _MSC_VER
#pragma warning(pop)
#endif

#include "libtorrent/peer_connection.hpp"
#include "libtorrent/bt_peer_connection.hpp"
#include "libtorrent/bencode.hpp"
#include "libtorrent/torrent.hpp"
#include "libtorrent/extensions.hpp"

#include "libtorrent/extensions/ut_pex.hpp"

namespace libtorrent { namespace
{
	const char extension_name[] = "ut_pex";

	enum
	{
		extension_index = 1,
		max_peer_entries = 100
	};

	bool send_peer(peer_connection const& p)
	{
		// don't send out peers that we haven't connected to
		// (that have connected to us)
		if (!p.is_local()) return false;
		// don't send out peers that we haven't successfully connected to
		if (p.is_connecting()) return false;
		return true;
	}

	struct ut_pex_plugin: torrent_plugin
	{
		ut_pex_plugin(torrent& t): m_torrent(t), m_1_minute(0) {}

		virtual boost::shared_ptr<peer_plugin> new_connection(peer_connection* pc);

		std::vector<char>& get_ut_pex_msg()
		{
			return m_ut_pex_msg;
		}

		// the second tick of the torrent
		// each minute the new lists of "added" + "added.f" and "dropped"
		// are calculated here and the pex message is created
		// each peer connection will use this message
		// max_peer_entries limits the packet size
		virtual void tick()
		{
			if (++m_1_minute < 60) return;

			m_1_minute = 0;

			entry pex;
			std::string& pla = pex["added"].string();
			std::string& pld = pex["dropped"].string();
			std::string& plf = pex["added.f"].string();
			std::string& pla6 = pex["added6"].string();
			std::string& pld6 = pex["dropped6"].string();
			std::string& plf6 = pex["added6.f"].string();
			std::back_insert_iterator<std::string> pla_out(pla);
			std::back_insert_iterator<std::string> pld_out(pld);
			std::back_insert_iterator<std::string> plf_out(plf);
			std::back_insert_iterator<std::string> pla6_out(pla6);
			std::back_insert_iterator<std::string> pld6_out(pld6);
			std::back_insert_iterator<std::string> plf6_out(plf6);

			std::set<tcp::endpoint> dropped;
			m_old_peers.swap(dropped);

			int num_added = 0;
			for (torrent::peer_iterator i = m_torrent.begin()
				, end(m_torrent.end()); i != end; ++i)
			{
				peer_connection* peer = *i;
				if (!send_peer(*peer)) continue;

				tcp::endpoint const& remote = peer->remote();
				m_old_peers.insert(remote);

				std::set<tcp::endpoint>::iterator di = dropped.find(remote);
				if (di == dropped.end())
				{
					// don't write too big of a package
					if (num_added >= max_peer_entries) break;

					// only send proper bittorrent peers
					bt_peer_connection* p = dynamic_cast<bt_peer_connection*>(peer);
					if (!p) continue;

					// no supported flags to set yet
					// 0x01 - peer supports encryption
					// 0x02 - peer is a seed
					int flags = p->is_seed() ? 2 : 0;
#ifndef TORRENT_DISABLE_ENCRYPTION
					flags |= p->supports_encryption() ? 1 : 0;
#endif
					// i->first was added since the last time
					if (remote.address().is_v4())
					{
						detail::write_endpoint(remote, pla_out);
						detail::write_uint8(flags, plf_out);
					}
					else
					{
						detail::write_endpoint(remote, pla6_out);
						detail::write_uint8(flags, plf6_out);
					}
					++num_added;
				}
				else
				{
					// this was in the previous message
					// so, it wasn't dropped
					dropped.erase(di);
				}
			}

			for (std::set<tcp::endpoint>::const_iterator i = dropped.begin()
				, end(dropped.end()); i != end; ++i)
			{
				if (i->address().is_v4())
					detail::write_endpoint(*i, pld_out);
				else
					detail::write_endpoint(*i, pld6_out);
			}

			m_ut_pex_msg.clear();
			bencode(std::back_inserter(m_ut_pex_msg), pex);
		}

	private:
		torrent& m_torrent;

		std::set<tcp::endpoint> m_old_peers;
		int m_1_minute;
		std::vector<char> m_ut_pex_msg;
	};

	struct ut_pex_peer_plugin : peer_plugin
	{
		ut_pex_peer_plugin(torrent& t, peer_connection& pc, ut_pex_plugin& tp)
			: m_torrent(t)
			, m_pc(pc)
			, m_tp(tp)
			, m_1_minute(0)
			, m_message_index(0)
			, m_first_time(true)
		{}

		virtual void add_handshake(entry& h)
		{
			entry& messages = h["m"];
			messages[extension_name] = extension_index;
		}

		virtual bool on_extension_handshake(entry const& h)
		{
			entry const& messages = h["m"];

			if (entry const* index = messages.find_key(extension_name))
			{
				m_message_index = index->integer();
				return true;
			}
			else
			{
				m_message_index = 0;
				return false;
			}
		}

		virtual bool on_extended(int length, int msg, buffer::const_interval body)
		{
			if (msg != extension_index) return false;
			if (m_message_index == 0) return false;

			if (length > 500 * 1024)
				throw protocol_error("uT peer exchange message larger than 500 kB");

			if (body.left() < length) return true;

			try
			{
				entry pex_msg = bdecode(body.begin, body.end);
				std::string const& peers = pex_msg["added"].string();
				std::string const& peer_flags = pex_msg["added.f"].string();

				int num_peers = peers.length() / 6;
				char const* in = peers.c_str();
				char const* fin = peer_flags.c_str();

				if (int(peer_flags.size()) != num_peers)
					return true;

				peer_id pid(0);
				policy& p = m_torrent.get_policy();
				for (int i = 0; i < num_peers; ++i)
				{
					tcp::endpoint adr = detail::read_v4_endpoint<tcp::endpoint>(in);
					char flags = detail::read_uint8(fin);
					p.peer_from_tracker(adr, pid, peer_info::pex, flags);
				}

				if (entry const* p6 = pex_msg.find_key("added6"))
				{
					std::string const& peers6 = p6->string();
					std::string const& peer6_flags = pex_msg["added6.f"].string();

					int num_peers = peers6.length() / 18;
					char const* in = peers6.c_str();
					char const* fin = peer6_flags.c_str();

					if (int(peer6_flags.size()) != num_peers)
						return true;

					peer_id pid(0);
					policy& p = m_torrent.get_policy();
					for (int i = 0; i < num_peers; ++i)
					{
						tcp::endpoint adr = detail::read_v6_endpoint<tcp::endpoint>(in);
						char flags = detail::read_uint8(fin);
						p.peer_from_tracker(adr, pid, peer_info::pex, flags);
					}
				}
			}
			catch (std::exception&)
			{
				throw protocol_error("invalid uT peer exchange message");
			}
			return true;
		}

		// the peers second tick
		// every minute we send a pex message
		virtual void tick()
		{
			if (!m_message_index) return;	// no handshake yet
			if (++m_1_minute <= 60) return;

			if (m_first_time)
			{
				send_ut_peer_list();
				m_first_time = false;
			}
			else
			{
				send_ut_peer_diff();
			}
			m_1_minute = 0;
		}

	private:

		void send_ut_peer_diff()
		{
			std::vector<char> const& pex_msg = m_tp.get_ut_pex_msg();

			buffer::interval i = m_pc.allocate_send_buffer(6 + pex_msg.size());

			detail::write_uint32(1 + 1 + pex_msg.size(), i.begin);
			detail::write_uint8(bt_peer_connection::msg_extended, i.begin);
			detail::write_uint8(m_message_index, i.begin);
			std::copy(pex_msg.begin(), pex_msg.end(), i.begin);
			i.begin += pex_msg.size();

			TORRENT_ASSERT(i.begin == i.end);
			m_pc.setup_send();
		}

		void send_ut_peer_list()
		{
			entry pex;
			// leave the dropped string empty
			pex["dropped"].string();
			std::string& pla = pex["added"].string();
			std::string& plf = pex["added.f"].string();
			pex["dropped6"].string();
			std::string& pla6 = pex["added6"].string();
			std::string& plf6 = pex["added6.f"].string();
			std::back_insert_iterator<std::string> pla_out(pla);
			std::back_insert_iterator<std::string> plf_out(plf);
			std::back_insert_iterator<std::string> pla6_out(pla6);
			std::back_insert_iterator<std::string> plf6_out(plf6);

			int num_added = 0;
			for (torrent::peer_iterator i = m_torrent.begin()
				, end(m_torrent.end()); i != end; ++i)
			{
				peer_connection* peer = *i;
				if (!send_peer(*peer)) continue;

				// don't write too big of a package
				if (num_added >= max_peer_entries) break;

				// only send proper bittorrent peers
				bt_peer_connection* p = dynamic_cast<bt_peer_connection*>(peer);
				if (!p) continue;

				// no supported flags to set yet
				// 0x01 - peer supports encryption
				// 0x02 - peer is a seed
				int flags = p->is_seed() ? 2 : 0;
#ifndef TORRENT_DISABLE_ENCRYPTION
				flags |= p->supports_encryption() ? 1 : 0;
#endif
				tcp::endpoint const& remote = peer->remote();
				// i->first was added since the last time
				if (remote.address().is_v4())
				{
					detail::write_endpoint(remote, pla_out);
					detail::write_uint8(flags, plf_out);
				}
				else
				{
					detail::write_endpoint(remote, pla6_out);
					detail::write_uint8(flags, plf6_out);
				}
				++num_added;
			}
			std::vector<char> pex_msg;
			bencode(std::back_inserter(pex_msg), pex);

			buffer::interval i = m_pc.allocate_send_buffer(6 + pex_msg.size());

			detail::write_uint32(1 + 1 + pex_msg.size(), i.begin);
			detail::write_uint8(bt_peer_connection::msg_extended, i.begin);
			detail::write_uint8(m_message_index, i.begin);
			std::copy(pex_msg.begin(), pex_msg.end(), i.begin);
			i.begin += pex_msg.size();

			TORRENT_ASSERT(i.begin == i.end);
			m_pc.setup_send();
		}

		torrent& m_torrent;
		peer_connection& m_pc;
		ut_pex_plugin& m_tp;
		int m_1_minute;
		int m_message_index;

		// this is initialized to true, and set to
		// false after the first pex message has been sent.
		// it is used to know if a diff message or a full
		// message should be sent.
		bool m_first_time;
	};

	boost::shared_ptr<peer_plugin> ut_pex_plugin::new_connection(peer_connection* pc)
	{
		bt_peer_connection* c = dynamic_cast<bt_peer_connection*>(pc);
		if (!c) return boost::shared_ptr<peer_plugin>();
		return boost::shared_ptr<peer_plugin>(new ut_pex_peer_plugin(m_torrent
			, *pc, *this));
	}
}}

namespace libtorrent
{

	boost::shared_ptr<torrent_plugin> create_ut_pex_plugin(torrent* t, void*)
	{
		if (t->torrent_file().priv())
		{
			return boost::shared_ptr<torrent_plugin>();
		}
		return boost::shared_ptr<torrent_plugin>(new ut_pex_plugin(*t));
	}

}

<commit_msg>ut_pex sends peers sooner<commit_after>/*

Copyright (c) 2006, MassaRoddel, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "libtorrent/pch.hpp"

#ifdef _MSC_VER
#pragma warning(push, 1)
#endif

#include <boost/shared_ptr.hpp>

#ifdef _MSC_VER
#pragma warning(pop)
#endif

#include "libtorrent/peer_connection.hpp"
#include "libtorrent/bt_peer_connection.hpp"
#include "libtorrent/bencode.hpp"
#include "libtorrent/torrent.hpp"
#include "libtorrent/extensions.hpp"

#include "libtorrent/extensions/ut_pex.hpp"

namespace libtorrent { namespace
{
	const char extension_name[] = "ut_pex";

	enum
	{
		extension_index = 1,
		max_peer_entries = 100
	};

	bool send_peer(peer_connection const& p)
	{
		// don't send out peers that we haven't connected to
		// (that have connected to us)
		if (!p.is_local()) return false;
		// don't send out peers that we haven't successfully connected to
		if (p.is_connecting()) return false;
		return true;
	}

	struct ut_pex_plugin: torrent_plugin
	{
		ut_pex_plugin(torrent& t): m_torrent(t), m_1_minute(55) {}

		virtual boost::shared_ptr<peer_plugin> new_connection(peer_connection* pc);

		std::vector<char>& get_ut_pex_msg()
		{
			return m_ut_pex_msg;
		}

		// the second tick of the torrent
		// each minute the new lists of "added" + "added.f" and "dropped"
		// are calculated here and the pex message is created
		// each peer connection will use this message
		// max_peer_entries limits the packet size
		virtual void tick()
		{
			if (++m_1_minute < 60) return;

			m_1_minute = 0;

			entry pex;
			std::string& pla = pex["added"].string();
			std::string& pld = pex["dropped"].string();
			std::string& plf = pex["added.f"].string();
			std::string& pla6 = pex["added6"].string();
			std::string& pld6 = pex["dropped6"].string();
			std::string& plf6 = pex["added6.f"].string();
			std::back_insert_iterator<std::string> pla_out(pla);
			std::back_insert_iterator<std::string> pld_out(pld);
			std::back_insert_iterator<std::string> plf_out(plf);
			std::back_insert_iterator<std::string> pla6_out(pla6);
			std::back_insert_iterator<std::string> pld6_out(pld6);
			std::back_insert_iterator<std::string> plf6_out(plf6);

			std::set<tcp::endpoint> dropped;
			m_old_peers.swap(dropped);

			int num_added = 0;
			for (torrent::peer_iterator i = m_torrent.begin()
				, end(m_torrent.end()); i != end; ++i)
			{
				peer_connection* peer = *i;
				if (!send_peer(*peer)) continue;

				tcp::endpoint const& remote = peer->remote();
				m_old_peers.insert(remote);

				std::set<tcp::endpoint>::iterator di = dropped.find(remote);
				if (di == dropped.end())
				{
					// don't write too big of a package
					if (num_added >= max_peer_entries) break;

					// only send proper bittorrent peers
					bt_peer_connection* p = dynamic_cast<bt_peer_connection*>(peer);
					if (!p) continue;

					// no supported flags to set yet
					// 0x01 - peer supports encryption
					// 0x02 - peer is a seed
					int flags = p->is_seed() ? 2 : 0;
#ifndef TORRENT_DISABLE_ENCRYPTION
					flags |= p->supports_encryption() ? 1 : 0;
#endif
					// i->first was added since the last time
					if (remote.address().is_v4())
					{
						detail::write_endpoint(remote, pla_out);
						detail::write_uint8(flags, plf_out);
					}
					else
					{
						detail::write_endpoint(remote, pla6_out);
						detail::write_uint8(flags, plf6_out);
					}
					++num_added;
				}
				else
				{
					// this was in the previous message
					// so, it wasn't dropped
					dropped.erase(di);
				}
			}

			for (std::set<tcp::endpoint>::const_iterator i = dropped.begin()
				, end(dropped.end()); i != end; ++i)
			{
				if (i->address().is_v4())
					detail::write_endpoint(*i, pld_out);
				else
					detail::write_endpoint(*i, pld6_out);
			}

			m_ut_pex_msg.clear();
			bencode(std::back_inserter(m_ut_pex_msg), pex);
		}

	private:
		torrent& m_torrent;

		std::set<tcp::endpoint> m_old_peers;
		int m_1_minute;
		std::vector<char> m_ut_pex_msg;
	};

	struct ut_pex_peer_plugin : peer_plugin
	{
		ut_pex_peer_plugin(torrent& t, peer_connection& pc, ut_pex_plugin& tp)
			: m_torrent(t)
			, m_pc(pc)
			, m_tp(tp)
			, m_1_minute(55)
			, m_message_index(0)
			, m_first_time(true)
		{}

		virtual void add_handshake(entry& h)
		{
			entry& messages = h["m"];
			messages[extension_name] = extension_index;
		}

		virtual bool on_extension_handshake(entry const& h)
		{
			entry const& messages = h["m"];

			if (entry const* index = messages.find_key(extension_name))
			{
				m_message_index = index->integer();
				return true;
			}
			else
			{
				m_message_index = 0;
				return false;
			}
		}

		virtual bool on_extended(int length, int msg, buffer::const_interval body)
		{
			if (msg != extension_index) return false;
			if (m_message_index == 0) return false;

			if (length > 500 * 1024)
				throw protocol_error("uT peer exchange message larger than 500 kB");

			if (body.left() < length) return true;

			try
			{
				entry pex_msg = bdecode(body.begin, body.end);
				std::string const& peers = pex_msg["added"].string();
				std::string const& peer_flags = pex_msg["added.f"].string();

				int num_peers = peers.length() / 6;
				char const* in = peers.c_str();
				char const* fin = peer_flags.c_str();

				if (int(peer_flags.size()) != num_peers)
					return true;

				peer_id pid(0);
				policy& p = m_torrent.get_policy();
				for (int i = 0; i < num_peers; ++i)
				{
					tcp::endpoint adr = detail::read_v4_endpoint<tcp::endpoint>(in);
					char flags = detail::read_uint8(fin);
					p.peer_from_tracker(adr, pid, peer_info::pex, flags);
				}

				if (entry const* p6 = pex_msg.find_key("added6"))
				{
					std::string const& peers6 = p6->string();
					std::string const& peer6_flags = pex_msg["added6.f"].string();

					int num_peers = peers6.length() / 18;
					char const* in = peers6.c_str();
					char const* fin = peer6_flags.c_str();

					if (int(peer6_flags.size()) != num_peers)
						return true;

					peer_id pid(0);
					policy& p = m_torrent.get_policy();
					for (int i = 0; i < num_peers; ++i)
					{
						tcp::endpoint adr = detail::read_v6_endpoint<tcp::endpoint>(in);
						char flags = detail::read_uint8(fin);
						p.peer_from_tracker(adr, pid, peer_info::pex, flags);
					}
				}
			}
			catch (std::exception&)
			{
				throw protocol_error("invalid uT peer exchange message");
			}
			return true;
		}

		// the peers second tick
		// every minute we send a pex message
		virtual void tick()
		{
			if (!m_message_index) return;	// no handshake yet
			if (++m_1_minute <= 60) return;

			if (m_first_time)
			{
				send_ut_peer_list();
				m_first_time = false;
			}
			else
			{
				send_ut_peer_diff();
			}
			m_1_minute = 0;
		}

	private:

		void send_ut_peer_diff()
		{
			std::vector<char> const& pex_msg = m_tp.get_ut_pex_msg();

			buffer::interval i = m_pc.allocate_send_buffer(6 + pex_msg.size());

			detail::write_uint32(1 + 1 + pex_msg.size(), i.begin);
			detail::write_uint8(bt_peer_connection::msg_extended, i.begin);
			detail::write_uint8(m_message_index, i.begin);
			std::copy(pex_msg.begin(), pex_msg.end(), i.begin);
			i.begin += pex_msg.size();

			TORRENT_ASSERT(i.begin == i.end);
			m_pc.setup_send();
		}

		void send_ut_peer_list()
		{
			entry pex;
			// leave the dropped string empty
			pex["dropped"].string();
			std::string& pla = pex["added"].string();
			std::string& plf = pex["added.f"].string();
			pex["dropped6"].string();
			std::string& pla6 = pex["added6"].string();
			std::string& plf6 = pex["added6.f"].string();
			std::back_insert_iterator<std::string> pla_out(pla);
			std::back_insert_iterator<std::string> plf_out(plf);
			std::back_insert_iterator<std::string> pla6_out(pla6);
			std::back_insert_iterator<std::string> plf6_out(plf6);

			int num_added = 0;
			for (torrent::peer_iterator i = m_torrent.begin()
				, end(m_torrent.end()); i != end; ++i)
			{
				peer_connection* peer = *i;
				if (!send_peer(*peer)) continue;

				// don't write too big of a package
				if (num_added >= max_peer_entries) break;

				// only send proper bittorrent peers
				bt_peer_connection* p = dynamic_cast<bt_peer_connection*>(peer);
				if (!p) continue;

				// no supported flags to set yet
				// 0x01 - peer supports encryption
				// 0x02 - peer is a seed
				int flags = p->is_seed() ? 2 : 0;
#ifndef TORRENT_DISABLE_ENCRYPTION
				flags |= p->supports_encryption() ? 1 : 0;
#endif
				tcp::endpoint const& remote = peer->remote();
				// i->first was added since the last time
				if (remote.address().is_v4())
				{
					detail::write_endpoint(remote, pla_out);
					detail::write_uint8(flags, plf_out);
				}
				else
				{
					detail::write_endpoint(remote, pla6_out);
					detail::write_uint8(flags, plf6_out);
				}
				++num_added;
			}
			std::vector<char> pex_msg;
			bencode(std::back_inserter(pex_msg), pex);

			buffer::interval i = m_pc.allocate_send_buffer(6 + pex_msg.size());

			detail::write_uint32(1 + 1 + pex_msg.size(), i.begin);
			detail::write_uint8(bt_peer_connection::msg_extended, i.begin);
			detail::write_uint8(m_message_index, i.begin);
			std::copy(pex_msg.begin(), pex_msg.end(), i.begin);
			i.begin += pex_msg.size();

			TORRENT_ASSERT(i.begin == i.end);
			m_pc.setup_send();
		}

		torrent& m_torrent;
		peer_connection& m_pc;
		ut_pex_plugin& m_tp;
		int m_1_minute;
		int m_message_index;

		// this is initialized to true, and set to
		// false after the first pex message has been sent.
		// it is used to know if a diff message or a full
		// message should be sent.
		bool m_first_time;
	};

	boost::shared_ptr<peer_plugin> ut_pex_plugin::new_connection(peer_connection* pc)
	{
		bt_peer_connection* c = dynamic_cast<bt_peer_connection*>(pc);
		if (!c) return boost::shared_ptr<peer_plugin>();
		return boost::shared_ptr<peer_plugin>(new ut_pex_peer_plugin(m_torrent
			, *pc, *this));
	}
}}

namespace libtorrent
{

	boost::shared_ptr<torrent_plugin> create_ut_pex_plugin(torrent* t, void*)
	{
		if (t->torrent_file().priv())
		{
			return boost::shared_ptr<torrent_plugin>();
		}
		return boost::shared_ptr<torrent_plugin>(new ut_pex_plugin(*t));
	}

}

<|endoftext|>


<commit_before>#include "cmm_socket_receiver.h"
#include "cmm_socket_sender.h"
#include <pthread.h>
#include "debug.h"
#include <vector>
using std::vector;

class ReadyIROB {
  public:
    bool operator()(PendingIROB *pi) {
        assert(pi);
        PendingReceiverIROB *pirob = static_cast<PendingReceiverIROB*>(pi);
        assert(pirob);
        return (pirob->is_complete() && pirob->is_released());
    }
};

CMMSocketReceiver::CMMSocketReceiver(CMMSocketImpl *sk_)
    : sk(sk_)
{
    handle(CMM_CONTROL_MSG_BEGIN_IROB, this,
           &CMMSocketReceiver::do_begin_irob);
    handle(CMM_CONTROL_MSG_END_IROB, this, &CMMSocketReceiver::do_end_irob);
    handle(CMM_CONTROL_MSG_IROB_CHUNK, this,
           &CMMSocketReceiver::do_irob_chunk);
    handle(CMM_CONTROL_MSG_NEW_INTERFACE, this,
           &CMMSocketReceiver::do_new_interface);
    handle(CMM_CONTROL_MSG_DOWN_INTERFACE, this,
           &CMMSocketReceiver::do_down_interface);
    handle(CMM_CONTROL_MSG_ACK, this, &CMMSocketReceiver::do_ack);
}

CMMSocketReceiver::~CMMSocketReceiver()
{
    PendingIROBHash::accessor ac;
    while (pending_irobs.any(ac)) {
        PendingIROB *victim = ac->second;
        pending_irobs.erase(ac);
        delete victim;
        ac.release();
    }
}

void
CMMSocketReceiver::dispatch(struct CMMSocketControlHdr hdr)
{
    dbgprintf("Got request\n%s\n", hdr.describe().c_str());
    CMMSocketScheduler<struct CMMSocketControlHdr>::dispatch(hdr);
}

void
CMMSocketReceiver::do_begin_irob(struct CMMSocketControlHdr hdr)
{
    assert(ntohs(hdr.type) == CMM_CONTROL_MSG_BEGIN_IROB);
    if (hdr.op.begin_irob.numdeps > 0) {
        assert(hdr.op.begin_irob.deps);
    }

    PendingIROB *pirob = new PendingReceiverIROB(hdr.op.begin_irob);
    PendingIROBHash::accessor ac;
    if (!pending_irobs.insert(ac, pirob)) {
        delete pirob;
        throw Exception::make("Tried to begin IROB that already exists",
                                  hdr);
    }

    if (hdr.op.begin_irob.numdeps > 0) {
        delete [] hdr.op.begin_irob.deps;
    }
}

void
CMMSocketReceiver::do_end_irob(struct CMMSocketControlHdr hdr)
{
    assert(ntohs(hdr.type) == CMM_CONTROL_MSG_END_IROB);
    PendingIROBHash::accessor ac;
    irob_id_t id = ntohl(hdr.op.end_irob.id);
    if (!pending_irobs.find(ac, id)) {
        if (pending_irobs.past_irob_exists(id)) {
            throw Exception::make("Tried to end committed IROB", hdr);
        } else {
            throw Exception::make("Tried to end nonexistent IROB", hdr);
        }
    }
    PendingIROB *pirob = ac->second;
    assert(pirob);
    if (!pirob->finish()) {
        throw Exception::make("Tried to end already-done IROB", hdr);
    }

    PendingReceiverIROB *prirob = static_cast<PendingReceiverIROB*>(pirob);
    pending_irobs.release_if_ready(prirob, ReadyIROB());
}

void
CMMSocketReceiver::do_irob_chunk(struct CMMSocketControlHdr hdr)
{
    assert(ntohs(hdr.type) == CMM_CONTROL_MSG_IROB_CHUNK);
    PendingIROBHash::accessor ac;
    irob_id_t id = ntohl(hdr.op.irob_chunk.id);
    if (!pending_irobs.find(ac, id)) {
        if (pending_irobs.past_irob_exists(id)) {
            throw Exception::make("Tried to add to committed IROB", hdr);
        } else {
            throw Exception::make("Tried to add to nonexistent IROB", hdr);
        }
    }
    struct irob_chunk_data chunk;
    chunk.id = id;
    chunk.seqno = ntohl(hdr.op.irob_chunk.seqno);
    chunk.datalen = ntohl(hdr.op.irob_chunk.datalen);
    chunk.data = hdr.op.irob_chunk.data;

    PendingIROB *pirob = ac->second;
    assert(pirob);
    PendingReceiverIROB *prirob = static_cast<PendingReceiverIROB*>(pirob);
    assert(prirob);
    if (!pirob->add_chunk(chunk)) {
        throw Exception::make("Tried to add to completed IROB", hdr);
    } else {
	dbgprintf("Successfully added chunk %d to IROB %d\n",
		  chunk.seqno, id);
    }
}

void
CMMSocketReceiver::do_new_interface(struct CMMSocketControlHdr hdr)
{
    assert(ntohs(hdr.type) == CMM_CONTROL_MSG_NEW_INTERFACE);
    struct net_interface iface = {hdr.op.new_interface.ip_addr,
                                  hdr.op.new_interface.labels};
    sk->setup(iface, false);
}

void
CMMSocketReceiver::do_down_interface(struct CMMSocketControlHdr hdr)
{
    assert(ntohs(hdr.type) == CMM_CONTROL_MSG_DOWN_INTERFACE);
    struct net_interface iface = {hdr.op.down_interface.ip_addr};
    sk->teardown(iface, false);
}

void
CMMSocketReceiver::do_ack(struct CMMSocketControlHdr hdr)
{
    assert(ntohs(hdr.type) == CMM_CONTROL_MSG_ACK);
    sk->sendr->ack_received(ntohl(hdr.op.ack.id), ntohl(hdr.op.ack.seqno));
}

/* This is where all the scheduling logic happens.
 * This function decides how to pass IROB data to the application.
 *
 * Additionally, this function is the only place we need
 * to worry about race conditions with the Receiver thread.
 * Thus, the locking discipline of that thread will be informed
 * by whatever reads and writes are necessary here.
 *
 * Initial stab at requirements:
 *   This thread definitely needs to read from the pending_irobs
 *   data structure, to figure out what data to pass to the
 *   application.  It also needs to update this data structure
 *   and the past_irobs set as IROBs are committed.
 * Thought: we could make this function read-only by using the
 *   msg_queue to cause the Receiver thread to do any needed updates.
 */
/* TODO: nonblocking mode */
ssize_t
CMMSocketReceiver::recv(void *bufp, size_t len, int flags, u_long *recv_labels)
{
    vector<PendingReceiverIROB *> pirobs;
    char *buf = (char*)bufp;

    ssize_t bytes_ready = 0;
    while ((size_t)bytes_ready < len) {
        PendingReceiverIROB *pirob = pending_irobs.get_ready_irob();
        assert(pirob); /* XXX: nonblocking version could return NULL */

        /* after the IROB is returned here, no other thread will
         * unsafely modify it.
         * XXX: this will not be true if we allow get_next_irob
         * to return released, incomplete IROBs.
         * We could fix that by simply having a sentinel chunk
         * on the concurrent_queue of chunks. */

        assert(pirob->is_released());
        assert(pirob->is_complete()); /* XXX: see get_next_irob */

        ssize_t bytes = pirob->numbytes();
        assert(bytes > 0);
        bytes_ready += bytes;

        pirobs.push_back(pirob);

        if (!pirob->is_complete()) {
            break;
        }
    }

    ssize_t bytes_passed = 0;
    bool partial_irob = false;
    for (size_t i = 0; i < pirobs.size(); i++) {
        PendingIROBHash::accessor ac;
        PendingReceiverIROB *pirob = pirobs[i];
        if (!pending_irobs.find(ac, pirob->id)) {
            assert(0);
        }
        assert(pirob == ac->second);
        if (i == 0) {
            if (recv_labels) {
                *recv_labels = pirob->send_labels;
            }
        }

        bytes_passed += pirob->read_data(buf + bytes_passed,
                                         len - bytes_passed);
        if (pirob->is_complete() && pirob->numbytes() == 0) {
            pending_irobs.erase(ac);
            pending_irobs.release_dependents(pirob, ReadyIROB());
            delete pirob;
        } else {
            if (!pirob->is_complete()) {
                /* This should still be the last one in the list,
                 * since it MUST finish before any IROB can be
                 * passed to the application. */
            }
            /* this should be true for at most the last IROB
             * in the vector */
            assert(!partial_irob);
            partial_irob = true;
            pending_irobs.partially_read(pirob);
        }
    }
    return bytes_passed;
}
<commit_msg>d'oh, missed the variable name.  prirob != pirob.<commit_after>#include "cmm_socket_receiver.h"
#include "cmm_socket_sender.h"
#include <pthread.h>
#include "debug.h"
#include <vector>
using std::vector;

class ReadyIROB {
  public:
    bool operator()(PendingIROB *pi) {
        assert(pi);
        PendingReceiverIROB *pirob = static_cast<PendingReceiverIROB*>(pi);
        assert(pirob);
        return (pirob->is_complete() && pirob->is_released());
    }
};

CMMSocketReceiver::CMMSocketReceiver(CMMSocketImpl *sk_)
    : sk(sk_)
{
    handle(CMM_CONTROL_MSG_BEGIN_IROB, this,
           &CMMSocketReceiver::do_begin_irob);
    handle(CMM_CONTROL_MSG_END_IROB, this, &CMMSocketReceiver::do_end_irob);
    handle(CMM_CONTROL_MSG_IROB_CHUNK, this,
           &CMMSocketReceiver::do_irob_chunk);
    handle(CMM_CONTROL_MSG_NEW_INTERFACE, this,
           &CMMSocketReceiver::do_new_interface);
    handle(CMM_CONTROL_MSG_DOWN_INTERFACE, this,
           &CMMSocketReceiver::do_down_interface);
    handle(CMM_CONTROL_MSG_ACK, this, &CMMSocketReceiver::do_ack);
}

CMMSocketReceiver::~CMMSocketReceiver()
{
    PendingIROBHash::accessor ac;
    while (pending_irobs.any(ac)) {
        PendingIROB *victim = ac->second;
        pending_irobs.erase(ac);
        delete victim;
        ac.release();
    }
}

void
CMMSocketReceiver::dispatch(struct CMMSocketControlHdr hdr)
{
    dbgprintf("Got request\n%s\n", hdr.describe().c_str());
    CMMSocketScheduler<struct CMMSocketControlHdr>::dispatch(hdr);
}

void
CMMSocketReceiver::do_begin_irob(struct CMMSocketControlHdr hdr)
{
    assert(ntohs(hdr.type) == CMM_CONTROL_MSG_BEGIN_IROB);
    if (hdr.op.begin_irob.numdeps > 0) {
        assert(hdr.op.begin_irob.deps);
    }

    PendingIROB *pirob = new PendingReceiverIROB(hdr.op.begin_irob);
    PendingIROBHash::accessor ac;
    if (!pending_irobs.insert(ac, pirob)) {
        delete pirob;
        throw Exception::make("Tried to begin IROB that already exists",
                                  hdr);
    }

    if (hdr.op.begin_irob.numdeps > 0) {
        delete [] hdr.op.begin_irob.deps;
    }
}

void
CMMSocketReceiver::do_end_irob(struct CMMSocketControlHdr hdr)
{
    assert(ntohs(hdr.type) == CMM_CONTROL_MSG_END_IROB);
    PendingIROBHash::accessor ac;
    irob_id_t id = ntohl(hdr.op.end_irob.id);
    if (!pending_irobs.find(ac, id)) {
        if (pending_irobs.past_irob_exists(id)) {
            throw Exception::make("Tried to end committed IROB", hdr);
        } else {
            throw Exception::make("Tried to end nonexistent IROB", hdr);
        }
    }
    PendingIROB *pirob = ac->second;
    assert(pirob);
    if (!pirob->finish()) {
        throw Exception::make("Tried to end already-done IROB", hdr);
    }

    PendingReceiverIROB *prirob = static_cast<PendingReceiverIROB*>(pirob);
    pending_irobs.release_if_ready(prirob, ReadyIROB());
}

void
CMMSocketReceiver::do_irob_chunk(struct CMMSocketControlHdr hdr)
{
    assert(ntohs(hdr.type) == CMM_CONTROL_MSG_IROB_CHUNK);
    PendingIROBHash::accessor ac;
    irob_id_t id = ntohl(hdr.op.irob_chunk.id);
    if (!pending_irobs.find(ac, id)) {
        if (pending_irobs.past_irob_exists(id)) {
            throw Exception::make("Tried to add to committed IROB", hdr);
        } else {
            throw Exception::make("Tried to add to nonexistent IROB", hdr);
        }
    }
    struct irob_chunk_data chunk;
    chunk.id = id;
    chunk.seqno = ntohl(hdr.op.irob_chunk.seqno);
    chunk.datalen = ntohl(hdr.op.irob_chunk.datalen);
    chunk.data = hdr.op.irob_chunk.data;

    PendingIROB *pirob = ac->second;
    assert(pirob);
    PendingReceiverIROB *prirob = static_cast<PendingReceiverIROB*>(pirob);
    assert(prirob);
    if (!prirob->add_chunk(chunk)) {
        throw Exception::make("Tried to add to completed IROB", hdr);
    } else {
	dbgprintf("Successfully added chunk %d to IROB %d\n",
		  chunk.seqno, id);
    }
}

void
CMMSocketReceiver::do_new_interface(struct CMMSocketControlHdr hdr)
{
    assert(ntohs(hdr.type) == CMM_CONTROL_MSG_NEW_INTERFACE);
    struct net_interface iface = {hdr.op.new_interface.ip_addr,
                                  hdr.op.new_interface.labels};
    sk->setup(iface, false);
}

void
CMMSocketReceiver::do_down_interface(struct CMMSocketControlHdr hdr)
{
    assert(ntohs(hdr.type) == CMM_CONTROL_MSG_DOWN_INTERFACE);
    struct net_interface iface = {hdr.op.down_interface.ip_addr};
    sk->teardown(iface, false);
}

void
CMMSocketReceiver::do_ack(struct CMMSocketControlHdr hdr)
{
    assert(ntohs(hdr.type) == CMM_CONTROL_MSG_ACK);
    sk->sendr->ack_received(ntohl(hdr.op.ack.id), ntohl(hdr.op.ack.seqno));
}

/* This is where all the scheduling logic happens.
 * This function decides how to pass IROB data to the application.
 *
 * Additionally, this function is the only place we need
 * to worry about race conditions with the Receiver thread.
 * Thus, the locking discipline of that thread will be informed
 * by whatever reads and writes are necessary here.
 *
 * Initial stab at requirements:
 *   This thread definitely needs to read from the pending_irobs
 *   data structure, to figure out what data to pass to the
 *   application.  It also needs to update this data structure
 *   and the past_irobs set as IROBs are committed.
 * Thought: we could make this function read-only by using the
 *   msg_queue to cause the Receiver thread to do any needed updates.
 */
/* TODO: nonblocking mode */
ssize_t
CMMSocketReceiver::recv(void *bufp, size_t len, int flags, u_long *recv_labels)
{
    vector<PendingReceiverIROB *> pirobs;
    char *buf = (char*)bufp;

    ssize_t bytes_ready = 0;
    while ((size_t)bytes_ready < len) {
        PendingReceiverIROB *pirob = pending_irobs.get_ready_irob();
        assert(pirob); /* XXX: nonblocking version could return NULL */

        /* after the IROB is returned here, no other thread will
         * unsafely modify it.
         * XXX: this will not be true if we allow get_next_irob
         * to return released, incomplete IROBs.
         * We could fix that by simply having a sentinel chunk
         * on the concurrent_queue of chunks. */

        assert(pirob->is_released());
        assert(pirob->is_complete()); /* XXX: see get_next_irob */

        ssize_t bytes = pirob->numbytes();
        assert(bytes > 0);
        bytes_ready += bytes;

        pirobs.push_back(pirob);

        if (!pirob->is_complete()) {
            break;
        }
    }

    ssize_t bytes_passed = 0;
    bool partial_irob = false;
    for (size_t i = 0; i < pirobs.size(); i++) {
        PendingIROBHash::accessor ac;
        PendingReceiverIROB *pirob = pirobs[i];
        if (!pending_irobs.find(ac, pirob->id)) {
            assert(0);
        }
        assert(pirob == ac->second);
        if (i == 0) {
            if (recv_labels) {
                *recv_labels = pirob->send_labels;
            }
        }

        bytes_passed += pirob->read_data(buf + bytes_passed,
                                         len - bytes_passed);
        if (pirob->is_complete() && pirob->numbytes() == 0) {
            pending_irobs.erase(ac);
            pending_irobs.release_dependents(pirob, ReadyIROB());
            delete pirob;
        } else {
            if (!pirob->is_complete()) {
                /* This should still be the last one in the list,
                 * since it MUST finish before any IROB can be
                 * passed to the application. */
            }
            /* this should be true for at most the last IROB
             * in the vector */
            assert(!partial_irob);
            partial_irob = true;
            pending_irobs.partially_read(pirob);
        }
    }
    return bytes_passed;
}
<|endoftext|>