<commit_before>#ifndef STAN_MATH_REV_FUN_AENERALIZED_INVERSE_HPP
#define STAN_MATH_REV_FUN_AENERALIZED_INVERSE_HPP

auto f = [](const auto& a) {
  return stan::math::generalized_inverse(a);
};
Eigen::MatrixXd A = Eigen::MatrixXd::Random(10, 10);
test_ad(f, A);
#include <stan/math/rev/core.hpp>
#include <stan/math/prim/err.hpp>
#include <stan/math/prim/fun/Eigen.hpp>
#include <stan/math/rev/fun/cholesky_decompose.hpp>
#include <stan/math/rev/fun/transpose.hpp>
#include <stan/math/rev/fun/tcrossprod.hpp>
#include <stan/math/rev/fun/crossprod.hpp>
#include <stan/math/rev/fun/inverse_spd.hpp>
#include <stan/math/rev/fun/mdivide_left_spd.hpp>
#include <stan/math/rev/fun/mdivide_right_spd.hpp>
#include <stan/math/rev/fun/inverse.hpp>

namespace stan {
namespace math {

/**
 * Reverse mode differentiation algorithm reference:
 *
 * The Differentiation of Pseudo-Inverses and Nonlinear Least Squares Problems Whose Variables Separate.
 * Author(s): G. H. Golub and V. Pereyra.
 * Source: SIAM Journal on Numerical Analysis, Vol. 10, No. 2 (Apr., 1973), pp. 413-432
 *
 * Equation 4.12 in the paper
 */
template <typename EigMat, require_eigen_vt<is_var, EigMat>* = nullptr>
inline auto generalized_inverse(const EigMat& A) {
  using value_t = value_type_t<EigMat>;
  if (A.size() == 0) {
    return {};
  }

  const auto n = A.rows();
  const auto m = A.cols();

  if (A.rows() == A.cols()) {
    return inverse(A);
  }
  if (n < m) {
    arena_t<plain_type_t<EigMat>> A_arena(A);
    arena_t<EigMat> inv_A(transpose(mdivide_left_spd(tcrossprod(A_arena.val()), A_arena.val())));
    reverse_pass_callback([A_arena, inv_A]() mutable {
      A_arena.adj() += -inv_A * A.adj() * inv_A +
            tcrossprod(inv_A) * A.adj().transpose() * (1 - A * inv_A.transpose()) +
            (1 - inv_A * A) * A.adj().transpose() * crossprod(inv_A());
    });
    return ret;
  } else {
    arena_t<plain_type_t<EigMat>> A_arena(A);
    arena_t<EigMat> inv_A(transpose(mdivide_right_spd(A_arena.val(), crossprod(A_arena.val())));
    reverse_pass_callback([A_arena, inv_A]() mutable {
      A_arena.adj() += -inv_A * A.adj() * inv_A +
            tcrossprod(inv_A) * A.adj().transpose() * (1 - A * inv_A.tranpose()) +
            (1 - inv_A * A) * A.adj().transpose() * crossprod(inv_A());
    });
    return ret;
  }
}

/**
 * Reverse mode differentiation algorithm reference:
 *
 * The Differentiation of Pseudo-Inverses and Nonlinear Least Squares Problems Whose Variables Separate.
 * Author(s): G. H. Golub and V. Pereyra.
 * Source: SIAM Journal on Numerical Analysis, Vol. 10, No. 2 (Apr., 1973), pp. 413-432
 *
 * Equation 4.12 in the paper
 */
template <typename EigMat, typename Scal, require_eigen_t<EigMat>* = nullptr,
          require_stan_scalar_t<Scal>* = nullptr>
inline Eigen::Matrix<value_type_t<EigMat>, EigMat::RowsAtCompileTime,
                     EigMat::ColsAtCompileTime>
generalized_inverse(const EigMat& A, const Scal& a) {
  using value_t = value_type_t<EigMat>;
  if (A.size() == 0) {
    return {};
  }

  const auto n = A.rows();
  const auto m = A.cols();

  if (A.rows() == A.cols()) {
    return inverse(A);
  }

  if (n < m) {
    arena_t<plain_type_t<EigMat>> A_arena(A);
    arena_t<EigMat> A_spd = tcrossprod(A_arena.val());
    A_spd.diagonal().array() += a;

    arena_t<EigMat> inv_A(transpose(mdivide_left_spd(A_spd, A_arena.val())));
    reverse_pass_callback([A_arena, inv_A]() mutable {
      A_arena.adj() += -inv_A * A.adj() * inv_A +
            tcrossprod(inv_A) * A.adj().transpose() * (1 - A * inv_A.transpose()) +
            (1 - inv_A * A) * A.adj().transpose() * crossprod(inv_A());
    });
    return ret;
  } else {
    arena_t<plain_type_t<EigMat>> A_arena(A);
    arena_t<EigMat> A_spd = crossprod(A_arena.val());
    A_spd.diagonal().array() += a;

    arena_t<plain_type_t<EigMat>> A_arena(A);
    arena_t<EigMat> inv_A(transpose(mdivide_right_spd(A_arena.val(), A_spd));
    reverse_pass_callback([A_arena, inv_A]() mutable {
      A_arena.adj() += -inv_A * A.adj() * inv_A +
            tcrossprod(inv_A) * A.adj().transpose() * (1 - A * inv_A.tranpose()) +
            (1 - inv_A * A) * A.adj().transpose() * crossprod(inv_A());
    });
    return ret;
  }
}

}  // namespace math
}  // namespace stan

#endif<commit_msg>[Jenkins] auto-formatting by clang-format version 6.0.0-1ubuntu2~16.04.1 (tags/RELEASE_600/final)<commit_after>#ifndef STAN_MATH_REV_FUN_AENERALIZED_INVERSE_HPP
#define STAN_MATH_REV_FUN_AENERALIZED_INVERSE_HPP

auto f = [](const auto& a) { return stan::math::generalized_inverse(a); };
Eigen::MatrixXd A = Eigen::MatrixXd::Random(10, 10);
test_ad(f, A);
#include <stan/math/rev/core.hpp>
#include <stan/math/prim/err.hpp>
#include <stan/math/prim/fun/Eigen.hpp>
#include <stan/math/rev/fun/cholesky_decompose.hpp>
#include <stan/math/rev/fun/transpose.hpp>
#include <stan/math/rev/fun/tcrossprod.hpp>
#include <stan/math/rev/fun/crossprod.hpp>
#include <stan/math/rev/fun/inverse_spd.hpp>
#include <stan/math/rev/fun/mdivide_left_spd.hpp>
#include <stan/math/rev/fun/mdivide_right_spd.hpp>
#include <stan/math/rev/fun/inverse.hpp>

namespace stan {
namespace math {

/**
 * Reverse mode differentiation algorithm reference:
 *
 * The Differentiation of Pseudo-Inverses and Nonlinear Least Squares Problems
 * Whose Variables Separate. Author(s): G. H. Golub and V. Pereyra. Source: SIAM
 * Journal on Numerical Analysis, Vol. 10, No. 2 (Apr., 1973), pp. 413-432
 *
 * Equation 4.12 in the paper
 */
template <typename EigMat, require_eigen_vt<is_var, EigMat>* = nullptr>
inline auto generalized_inverse(const EigMat& A) {
  using value_t = value_type_t<EigMat>;
  if (A.size() == 0) {
    return {};
  }

  const auto n = A.rows();
  const auto m = A.cols();

  if (A.rows() == A.cols()) {
    return inverse(A);
  }
  if (n < m) {
    arena_t<plain_type_t<EigMat>> A_arena(A);
    arena_t<EigMat> inv_A(
        transpose(mdivide_left_spd(tcrossprod(A_arena.val()), A_arena.val())));
    reverse_pass_callback([A_arena, inv_A]() mutable {
      A_arena.adj()
          += -inv_A * A.adj() * inv_A
             + tcrossprod(inv_A) * A.adj().transpose()
                   * (1 - A * inv_A.transpose())
             + (1 - inv_A * A) * A.adj().transpose() * crossprod(inv_A());
    });
    return ret;
  } else {
    arena_t<plain_type_t<EigMat>> A_arena(A);
    arena_t<EigMat> inv_A(transpose(mdivide_right_spd(A_arena.val(), crossprod(A_arena.val())));
    reverse_pass_callback([A_arena, inv_A]() mutable {
      A_arena.adj()
          += -inv_A * A.adj() * inv_A
             + tcrossprod(inv_A) * A.adj().transpose()
                   * (1 - A * inv_A.tranpose())
             + (1 - inv_A * A) * A.adj().transpose() * crossprod(inv_A());
    });
    return ret;
  }
}

/**
 * Reverse mode differentiation algorithm reference:
 *
 * The Differentiation of Pseudo-Inverses and Nonlinear Least Squares Problems
 * Whose Variables Separate. Author(s): G. H. Golub and V. Pereyra. Source: SIAM
 * Journal on Numerical Analysis, Vol. 10, No. 2 (Apr., 1973), pp. 413-432
 *
 * Equation 4.12 in the paper
 */
template <typename EigMat, typename Scal, require_eigen_t<EigMat>* = nullptr,
          require_stan_scalar_t<Scal>* = nullptr>
inline Eigen::Matrix<value_type_t<EigMat>, EigMat::RowsAtCompileTime,
                     EigMat::ColsAtCompileTime>
generalized_inverse(const EigMat& A, const Scal& a) {
  using value_t = value_type_t<EigMat>;
  if (A.size() == 0) {
    return {};
  }

  const auto n = A.rows();
  const auto m = A.cols();

  if (A.rows() == A.cols()) {
    return inverse(A);
  }

  if (n < m) {
    arena_t<plain_type_t<EigMat>> A_arena(A);
    arena_t<EigMat> A_spd = tcrossprod(A_arena.val());
    A_spd.diagonal().array() += a;

    arena_t<EigMat> inv_A(transpose(mdivide_left_spd(A_spd, A_arena.val())));
    reverse_pass_callback([A_arena, inv_A]() mutable {
      A_arena.adj()
          += -inv_A * A.adj() * inv_A
             + tcrossprod(inv_A) * A.adj().transpose()
                   * (1 - A * inv_A.transpose())
             + (1 - inv_A * A) * A.adj().transpose() * crossprod(inv_A());
    });
    return ret;
  } else {
    arena_t<plain_type_t<EigMat>> A_arena(A);
    arena_t<EigMat> A_spd = crossprod(A_arena.val());
    A_spd.diagonal().array() += a;

    arena_t<plain_type_t<EigMat>> A_arena(A);
    arena_t<EigMat> inv_A(transpose(mdivide_right_spd(A_arena.val(), A_spd));
    reverse_pass_callback([A_arena, inv_A]() mutable {
      A_arena.adj()
          += -inv_A * A.adj() * inv_A
             + tcrossprod(inv_A) * A.adj().transpose()
                   * (1 - A * inv_A.tranpose())
             + (1 - inv_A * A) * A.adj().transpose() * crossprod(inv_A());
    });
    return ret;
  }
}

}  // namespace math
}  // namespace stan

#endif<|endoftext|>


<commit_before>#include "address.h"
#include "structural_node.h"
#include "program.h"

TLANG_NAMESPACE_BEGIN

// This part should be generated by the compiler
// The tree is built in a bottom-up order, so that upper level can learn about
// the size etc. of children.

/*
using placeholder_u = real;
using placeholder_v = real;
using node1 = forked<placeholder_u, placeholder_v>;
using root = fixed<32, node1>;

root r;

// Global look_up functions that take a data structure instance, and an index
// bit mixing is done here.
real &look_up_u(root &r, int i, int j, int k) {
  auto &n1 = r.look_up(i * 32 + j);
  auto &n2 = n1.get<1>();
  return n2;
}
*/

TLANG_NAMESPACE_END
<commit_msg>removed address.cpp<commit_after><|endoftext|>


<commit_before>/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2014. All Rights Reserved.                             */
/* Open Source Software - may be modified and shared by FRC teams. The code   */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project.                                                               */
/*----------------------------------------------------------------------------*/

#include "WPILib.h"
#include "gtest/gtest.h"
#include "TestBench.h"

static constexpr double kMotorTime = 0.5;

static constexpr double kEncoderSettlingTime = 0.25;
static constexpr double kEncoderPositionTolerance = 5.0/360.0; // +/-5 degrees

static constexpr double kPotentiometerSettlingTime = 0.05;
static constexpr double kPotentiometerPositionTolerance = 10.0/360.0; // +/-10 degrees

// TODO test coverage for CANJaguar
class CANJaguarTest : public testing::Test {
protected:
    CANJaguar *m_jaguar;
    DigitalOutput *m_fakeForwardLimit, *m_fakeReverseLimit;
    AnalogOutput *m_fakePotentiometer;

    virtual void SetUp() {
        m_jaguar = new CANJaguar(TestBench::kCANJaguarID);

        m_fakeForwardLimit = new DigitalOutput(TestBench::kFakeJaguarForwardLimit);
        m_fakeForwardLimit->Set(0);

        m_fakeReverseLimit = new DigitalOutput(TestBench::kFakeJaguarReverseLimit);
        m_fakeReverseLimit->Set(0);

        m_fakePotentiometer = new AnalogOutput(TestBench::kFakeJaguarPotentiometer);
        m_fakePotentiometer->SetVoltage(0.0f);

        /* The motor might still have momentum from the previous test. */
        //Wait(kEncoderSettlingTime);
    }

    virtual void TearDown() {
        delete m_jaguar;
        delete m_fakeForwardLimit;
        delete m_fakeReverseLimit;
        delete m_fakePotentiometer;
    }
};

/*TEST_F(CANJaguarTest, QuickTest) {
    m_jaguar->SetPercentMode(CANJaguar::Encoder, 360);

    while(DriverStation::GetInstance()->IsEnabled()) {
        std::cout << m_jaguar->GetPosition() << std::endl;
        std::cout << m_jaguar->GetSpeed() << std::endl;
        Wait(0.02);
    }
}*/

/**
 * Test if we can drive the motor in percentage mode and get a position back
 */
TEST_F(CANJaguarTest, PercentForwards) {
    m_jaguar->SetPercentMode(CANJaguar::QuadEncoder, 360);
    m_jaguar->EnableControl();
    m_jaguar->Set(0.0f);

    /* The motor might still have momentum from the previous test. */
    Wait(kEncoderSettlingTime);

    double initialPosition = m_jaguar->GetPosition();

    /* Drive the speed controller briefly to move the encoder */
    m_jaguar->Set(1.0f);
    Wait(kMotorTime);
    m_jaguar->Set(0.0f);

    /* The position should have increased */
    EXPECT_GT(m_jaguar->GetPosition(), initialPosition)
        << "CAN Jaguar position should have increased after the motor moved";
}

/**
 * Test if we can drive the motor backwards in percentage mode and get a
 * position back
 */
TEST_F(CANJaguarTest, PercentReverse) {
    m_jaguar->SetPercentMode(CANJaguar::QuadEncoder, 360);
    m_jaguar->EnableControl();
    m_jaguar->Set(0.0f);

    /* The motor might still have momentum from the previous test. */
    Wait(kEncoderSettlingTime);

    double initialPosition = m_jaguar->GetPosition();

    /* Drive the speed controller briefly to move the encoder */
    m_jaguar->Set(-1.0f);
    Wait(kMotorTime);
    m_jaguar->Set(0.0f);

    /* The position should have decreased */
    EXPECT_LT(m_jaguar->GetPosition(), initialPosition)
        << "CAN Jaguar position should have decreased after the motor moved";
}

/**
 * Test if we can set a position and reach that position with PID control on
 * the Jaguar.
 */
TEST_F(CANJaguarTest, EncoderPositionPID) {
    m_jaguar->SetPositionMode(CANJaguar::QuadEncoder, 360, 5.0f, 0.1f, 2.0f);
    m_jaguar->EnableControl();

    double setpoint = m_jaguar->GetPosition() + 10.0f;

    /* It should get to the setpoint within 10 seconds */
    m_jaguar->Set(setpoint);
    Wait(10.0f);

    EXPECT_NEAR(setpoint, m_jaguar->GetPosition(), kEncoderPositionTolerance)
        << "CAN Jaguar should have reached setpoint with PID control";
}

/**
 * Test if we can get a position in potentiometer mode, using an analog output
 * as a fake potentiometer.
 */
TEST_F(CANJaguarTest, FakePotentiometerPosition) {
    m_jaguar->SetPercentMode(CANJaguar::Potentiometer);
    m_jaguar->EnableControl();

    m_fakePotentiometer->SetVoltage(0.0f);
    Wait(kPotentiometerSettlingTime);
    EXPECT_NEAR(m_fakePotentiometer->GetVoltage() / 3.0f, m_jaguar->GetPosition(), kPotentiometerPositionTolerance)
        << "CAN Jaguar should have returned the potentiometer position set by the analog output";

    m_fakePotentiometer->SetVoltage(1.0f);
    Wait(kPotentiometerSettlingTime);
    EXPECT_NEAR(m_fakePotentiometer->GetVoltage() / 3.0f, m_jaguar->GetPosition(), kPotentiometerPositionTolerance)
        << "CAN Jaguar should have returned the potentiometer position set by the analog output";

    m_fakePotentiometer->SetVoltage(2.0f);
    Wait(kPotentiometerSettlingTime);
    EXPECT_NEAR(m_fakePotentiometer->GetVoltage() / 3.0f, m_jaguar->GetPosition(), kPotentiometerPositionTolerance)
        << "CAN Jaguar should have returned the potentiometer position set by the analog output";

    m_fakePotentiometer->SetVoltage(3.0f);
    Wait(kPotentiometerSettlingTime);
    EXPECT_NEAR(m_fakePotentiometer->GetVoltage() / 3.0f, m_jaguar->GetPosition(), kPotentiometerPositionTolerance)
        << "CAN Jaguar should have returned the potentiometer position set by the analog output";
}

/**
 * Test if we can limit the Jaguar to only moving in reverse with a fake
 * limit switch.
 */
TEST_F(CANJaguarTest, FakeLimitSwitchForwards) {
    m_jaguar->SetPercentMode(CANJaguar::QuadEncoder, 360);
    m_jaguar->ConfigLimitMode(CANJaguar::kLimitMode_SwitchInputsOnly);
    m_fakeForwardLimit->Set(1);
    m_fakeReverseLimit->Set(0);
    m_jaguar->EnableControl();

    m_jaguar->Set(0.0f);
    Wait(kEncoderSettlingTime);

    /* Make sure we limits are recognized by the Jaguar. */
    ASSERT_FALSE(m_jaguar->GetForwardLimitOK());
    ASSERT_TRUE(m_jaguar->GetReverseLimitOK());

    double initialPosition = m_jaguar->GetPosition();

    /* Drive the speed controller briefly to move the encoder.  If the limit
         switch is recognized, it shouldn't actually move. */
    m_jaguar->Set(1.0f);
    Wait(kMotorTime);
    m_jaguar->Set(0.0f);

    /* The position should be the same, since the limit switch was on. */
    EXPECT_NEAR(initialPosition, m_jaguar->GetPosition(), kEncoderPositionTolerance)
        << "CAN Jaguar should not have moved with the limit switch pressed";

    /* Drive the speed controller in the other direction.  It should actually
         move, since only the forward switch is activated.*/
    m_jaguar->Set(-1.0f);
    Wait(kMotorTime);
    m_jaguar->Set(0.0f);

    /* The position should have decreased */
    EXPECT_LT(m_jaguar->GetPosition(), initialPosition)
        << "CAN Jaguar should have moved in reverse while the forward limit was on";
}

/**
 * Test if we can limit the Jaguar to only moving forwards with a fake limit
 * switch.
 */
TEST_F(CANJaguarTest, FakeLimitSwitchReverse) {
    m_jaguar->SetPercentMode(CANJaguar::QuadEncoder, 360);
    m_jaguar->ConfigLimitMode(CANJaguar::kLimitMode_SwitchInputsOnly);
    m_fakeForwardLimit->Set(0);
    m_fakeReverseLimit->Set(1);
    m_jaguar->EnableControl();

    m_jaguar->Set(0.0f);
    Wait(kEncoderSettlingTime);

    /* Make sure we limits are recognized by the Jaguar. */
    ASSERT_TRUE(m_jaguar->GetForwardLimitOK());
    ASSERT_FALSE(m_jaguar->GetReverseLimitOK());

    double initialPosition = m_jaguar->GetPosition();

    /* Drive the speed controller backwards briefly to move the encoder.  If
         the limit switch is recognized, it shouldn't actually move. */
    m_jaguar->Set(-1.0f);
    Wait(kMotorTime);
    m_jaguar->Set(0.0f);

    /* The position should be the same, since the limit switch was on. */
    EXPECT_NEAR(initialPosition, m_jaguar->GetPosition(), kEncoderPositionTolerance)
        << "CAN Jaguar should not have moved with the limit switch pressed";

    Wait(kEncoderSettlingTime);

    /* Drive the speed controller in the other direction.  It should actually
         move, since only the reverse switch is activated.*/
    m_jaguar->Set(1.0f);
    Wait(kMotorTime);
    m_jaguar->Set(0.0f);

    /* The position should have increased */
    EXPECT_GT(m_jaguar->GetPosition(), initialPosition)
        << "CAN Jaguar should have moved forwards while the reverse limit was on";
}
<commit_msg>Added a C++ CANJaguar test for initial status data<commit_after>/*----------------------------------------------------------------------------*/
/* Copyright (c) FIRST 2014. All Rights Reserved.                             */
/* Open Source Software - may be modified and shared by FRC teams. The code   */
/* must be accompanied by the FIRST BSD license file in the root directory of */
/* the project.                                                               */
/*----------------------------------------------------------------------------*/

#include "WPILib.h"
#include "gtest/gtest.h"
#include "TestBench.h"

static constexpr double kExpectedBusVoltage = 14.0;
static constexpr double kExpectedTemperature = 25.0;

static constexpr double kMotorTime = 0.5;

static constexpr double kEncoderSettlingTime = 0.25;
static constexpr double kEncoderPositionTolerance = 5.0/360.0; // +/-5 degrees

static constexpr double kPotentiometerSettlingTime = 0.05;
static constexpr double kPotentiometerPositionTolerance = 10.0/360.0; // +/-10 degrees

// TODO test coverage for CANJaguar
class CANJaguarTest : public testing::Test {
protected:
    CANJaguar *m_jaguar;
    DigitalOutput *m_fakeForwardLimit, *m_fakeReverseLimit;
    AnalogOutput *m_fakePotentiometer;

    virtual void SetUp() {
        m_jaguar = new CANJaguar(TestBench::kCANJaguarID);

        m_fakeForwardLimit = new DigitalOutput(TestBench::kFakeJaguarForwardLimit);
        m_fakeForwardLimit->Set(0);

        m_fakeReverseLimit = new DigitalOutput(TestBench::kFakeJaguarReverseLimit);
        m_fakeReverseLimit->Set(0);

        m_fakePotentiometer = new AnalogOutput(TestBench::kFakeJaguarPotentiometer);
        m_fakePotentiometer->SetVoltage(0.0f);

        /* The motor might still have momentum from the previous test. */
        //Wait(kEncoderSettlingTime);
    }

    virtual void TearDown() {
        delete m_jaguar;
        delete m_fakeForwardLimit;
        delete m_fakeReverseLimit;
        delete m_fakePotentiometer;
    }
};

/*TEST_F(CANJaguarTest, QuickTest) {
    m_jaguar->SetSpeedMode(CANJaguar::Encoder, 360, 0.0, 0.0, 0.0);

    while(DriverStation::GetInstance()->IsEnabled()) {
        std::cout << m_jaguar->GetPosition() << std::endl;
        std::cout << m_jaguar->GetSpeed() << std::endl;
        std::cout << m_jaguar->GetTemperature() << std::endl;
        Wait(0.02);
    }
}*/

/**
 * Checks the default status data for reasonable values to confirm that we're
 * really getting status data from the Jaguar.
 */
TEST_F(CANJaguarTest, InitialStatus) {
    m_jaguar->SetPercentMode();

    EXPECT_NEAR(m_jaguar->GetBusVoltage(), kExpectedBusVoltage, 3.0)
        << "Bus voltage is not a plausible value.";

    EXPECT_FLOAT_EQ(m_jaguar->GetOutputVoltage(), 0.0)
        << "Output voltage is non-zero.";

    EXPECT_FLOAT_EQ(m_jaguar->GetOutputCurrent(), 0.0)
        << "Output current is non-zero.";

    EXPECT_NEAR(m_jaguar->GetTemperature(), kTempe, 5.0)
        << "Temperature is not a plausible value.";

    EXPECT_EQ(m_jaguar->GetFaults(), 0)
        << "Jaguar has one or more fault set.";
}

/**
 * Test if we can drive the motor in percentage mode and get a position back
 */
TEST_F(CANJaguarTest, PercentForwards) {
    m_jaguar->SetPercentMode(CANJaguar::QuadEncoder, 360);
    m_jaguar->EnableControl();
    m_jaguar->Set(0.0f);

    /* The motor might still have momentum from the previous test. */
    Wait(kEncoderSettlingTime);

    double initialPosition = m_jaguar->GetPosition();

    /* Drive the speed controller briefly to move the encoder */
    m_jaguar->Set(1.0f);
    Wait(kMotorTime);
    m_jaguar->Set(0.0f);

    /* The position should have increased */
    EXPECT_GT(m_jaguar->GetPosition(), initialPosition)
        << "CAN Jaguar position should have increased after the motor moved";
}

/**
 * Test if we can drive the motor backwards in percentage mode and get a
 * position back
 */
TEST_F(CANJaguarTest, PercentReverse) {
    m_jaguar->SetPercentMode(CANJaguar::QuadEncoder, 360);
    m_jaguar->EnableControl();
    m_jaguar->Set(0.0f);

    /* The motor might still have momentum from the previous test. */
    Wait(kEncoderSettlingTime);

    double initialPosition = m_jaguar->GetPosition();

    /* Drive the speed controller briefly to move the encoder */
    m_jaguar->Set(-1.0f);
    Wait(kMotorTime);
    m_jaguar->Set(0.0f);

    /* The position should have decreased */
    EXPECT_LT(m_jaguar->GetPosition(), initialPosition)
        << "CAN Jaguar position should have decreased after the motor moved";
}

/**
 * Test if we can set a position and reach that position with PID control on
 * the Jaguar.
 */
TEST_F(CANJaguarTest, EncoderPositionPID) {
    m_jaguar->SetPositionMode(CANJaguar::QuadEncoder, 360, 5.0f, 0.1f, 2.0f);
    m_jaguar->EnableControl();

    double setpoint = m_jaguar->GetPosition() + 10.0f;

    /* It should get to the setpoint within 10 seconds */
    m_jaguar->Set(setpoint);
    Wait(10.0f);

    EXPECT_NEAR(setpoint, m_jaguar->GetPosition(), kEncoderPositionTolerance)
        << "CAN Jaguar should have reached setpoint with PID control";
}

/**
 * Test if we can get a position in potentiometer mode, using an analog output
 * as a fake potentiometer.
 */
TEST_F(CANJaguarTest, FakePotentiometerPosition) {
    m_jaguar->SetPercentMode(CANJaguar::Potentiometer);
    m_jaguar->EnableControl();

    m_fakePotentiometer->SetVoltage(0.0f);
    Wait(kPotentiometerSettlingTime);
    EXPECT_NEAR(m_fakePotentiometer->GetVoltage() / 3.0f, m_jaguar->GetPosition(), kPotentiometerPositionTolerance)
        << "CAN Jaguar should have returned the potentiometer position set by the analog output";

    m_fakePotentiometer->SetVoltage(1.0f);
    Wait(kPotentiometerSettlingTime);
    EXPECT_NEAR(m_fakePotentiometer->GetVoltage() / 3.0f, m_jaguar->GetPosition(), kPotentiometerPositionTolerance)
        << "CAN Jaguar should have returned the potentiometer position set by the analog output";

    m_fakePotentiometer->SetVoltage(2.0f);
    Wait(kPotentiometerSettlingTime);
    EXPECT_NEAR(m_fakePotentiometer->GetVoltage() / 3.0f, m_jaguar->GetPosition(), kPotentiometerPositionTolerance)
        << "CAN Jaguar should have returned the potentiometer position set by the analog output";

    m_fakePotentiometer->SetVoltage(3.0f);
    Wait(kPotentiometerSettlingTime);
    EXPECT_NEAR(m_fakePotentiometer->GetVoltage() / 3.0f, m_jaguar->GetPosition(), kPotentiometerPositionTolerance)
        << "CAN Jaguar should have returned the potentiometer position set by the analog output";
}

/**
 * Test if we can limit the Jaguar to only moving in reverse with a fake
 * limit switch.
 */
TEST_F(CANJaguarTest, FakeLimitSwitchForwards) {
    m_jaguar->SetPercentMode(CANJaguar::QuadEncoder, 360);
    m_jaguar->ConfigLimitMode(CANJaguar::kLimitMode_SwitchInputsOnly);
    m_fakeForwardLimit->Set(1);
    m_fakeReverseLimit->Set(0);
    m_jaguar->EnableControl();

    m_jaguar->Set(0.0f);
    Wait(kEncoderSettlingTime);

    /* Make sure we limits are recognized by the Jaguar. */
    ASSERT_FALSE(m_jaguar->GetForwardLimitOK());
    ASSERT_TRUE(m_jaguar->GetReverseLimitOK());

    double initialPosition = m_jaguar->GetPosition();

    /* Drive the speed controller briefly to move the encoder.  If the limit
         switch is recognized, it shouldn't actually move. */
    m_jaguar->Set(1.0f);
    Wait(kMotorTime);
    m_jaguar->Set(0.0f);

    /* The position should be the same, since the limit switch was on. */
    EXPECT_NEAR(initialPosition, m_jaguar->GetPosition(), kEncoderPositionTolerance)
        << "CAN Jaguar should not have moved with the limit switch pressed";

    /* Drive the speed controller in the other direction.  It should actually
         move, since only the forward switch is activated.*/
    m_jaguar->Set(-1.0f);
    Wait(kMotorTime);
    m_jaguar->Set(0.0f);

    /* The position should have decreased */
    EXPECT_LT(m_jaguar->GetPosition(), initialPosition)
        << "CAN Jaguar should have moved in reverse while the forward limit was on";
}

/**
 * Test if we can limit the Jaguar to only moving forwards with a fake limit
 * switch.
 */
TEST_F(CANJaguarTest, FakeLimitSwitchReverse) {
    m_jaguar->SetPercentMode(CANJaguar::QuadEncoder, 360);
    m_jaguar->ConfigLimitMode(CANJaguar::kLimitMode_SwitchInputsOnly);
    m_fakeForwardLimit->Set(0);
    m_fakeReverseLimit->Set(1);
    m_jaguar->EnableControl();

    m_jaguar->Set(0.0f);
    Wait(kEncoderSettlingTime);

    /* Make sure we limits are recognized by the Jaguar. */
    ASSERT_TRUE(m_jaguar->GetForwardLimitOK());
    ASSERT_FALSE(m_jaguar->GetReverseLimitOK());

    double initialPosition = m_jaguar->GetPosition();

    /* Drive the speed controller backwards briefly to move the encoder.  If
         the limit switch is recognized, it shouldn't actually move. */
    m_jaguar->Set(-1.0f);
    Wait(kMotorTime);
    m_jaguar->Set(0.0f);

    /* The position should be the same, since the limit switch was on. */
    EXPECT_NEAR(initialPosition, m_jaguar->GetPosition(), kEncoderPositionTolerance)
        << "CAN Jaguar should not have moved with the limit switch pressed";

    Wait(kEncoderSettlingTime);

    /* Drive the speed controller in the other direction.  It should actually
         move, since only the reverse switch is activated.*/
    m_jaguar->Set(1.0f);
    Wait(kMotorTime);
    m_jaguar->Set(0.0f);

    /* The position should have increased */
    EXPECT_GT(m_jaguar->GetPosition(), initialPosition)
        << "CAN Jaguar should have moved forwards while the reverse limit was on";
}
<|endoftext|>


<commit_before>#ifndef _CPPUHELPER_WEAK_HXX_
#include <cppuhelper/weak.hxx>
#endif
#ifndef _OSL_MUTEX_HXX_
#include <osl/mutex.hxx>
#endif
#ifndef _COM_SUN_STAR_IO_XINPUTSTREAM_HPP_
#include <com/sun/star/io/XInputStream.hpp>
#endif
#ifndef _COM_SUN_STAR_IO_XSEEKABLE_HPP_
#include <com/sun/star/io/XSeekable.hpp>
#endif

namespace chelp {

    class BufferedInputStream
        : public cppu::OWeakObject,
          public com::sun::star::io::XInputStream,
          public com::sun::star::io::XSeekable
    {
    private:

        sal_Int32      m_nBufferLocation;
        sal_Int32      m_nBufferSize;
        sal_Int8      *m_pBuffer;
        osl::Mutex     m_aMutex;

    public:

        BufferedInputStream(
            const com::sun::star::uno::Reference<com::sun::star::io::XInputStream>& xInputStream);

        ~BufferedInputStream();

        virtual com::sun::star::uno::Any SAL_CALL
        queryInterface( const com::sun::star::uno::Type& rType )
            throw( com::sun::star::uno::RuntimeException );

        virtual void SAL_CALL acquire( void ) throw();

        virtual void SAL_CALL release( void ) throw();

        virtual sal_Int32 SAL_CALL readBytes( com::sun::star::uno::Sequence< sal_Int8 >& aData,
                                              sal_Int32 nBytesToRead )
            throw( com::sun::star::io::NotConnectedException,
                   com::sun::star::io::BufferSizeExceededException,
                   com::sun::star::io::IOException,
                   com::sun::star::uno::RuntimeException );

        virtual sal_Int32 SAL_CALL readSomeBytes( com::sun::star::uno::Sequence< sal_Int8 >& aData,
                                                  sal_Int32 nMaxBytesToRead )
            throw( com::sun::star::io::NotConnectedException,
                   com::sun::star::io::BufferSizeExceededException,
                   com::sun::star::io::IOException,
                   com::sun::star::uno::RuntimeException );

        virtual void SAL_CALL skipBytes( sal_Int32 nBytesToSkip )
            throw( com::sun::star::io::NotConnectedException,
                   com::sun::star::io::BufferSizeExceededException,
                   com::sun::star::io::IOException,
                   com::sun::star::uno::RuntimeException );

        virtual sal_Int32 SAL_CALL available( void )
            throw( com::sun::star::io::NotConnectedException,
                   com::sun::star::io::IOException,
                   com::sun::star::uno::RuntimeException );

        virtual void SAL_CALL closeInput( void )
            throw( com::sun::star::io::NotConnectedException,
                   com::sun::star::io::IOException,
                   com::sun::star::uno::RuntimeException );

        virtual void SAL_CALL seek( sal_Int64 location )
            throw( com::sun::star::lang::IllegalArgumentException,
                   com::sun::star::io::IOException,
                   com::sun::star::uno::RuntimeException );

        virtual sal_Int64 SAL_CALL getPosition( void )
            throw( com::sun::star::io::IOException,
                   com::sun::star::uno::RuntimeException );

        virtual sal_Int64 SAL_CALL getLength( void )
            throw( com::sun::star::io::IOException,
                   com::sun::star::uno::RuntimeException );
    };

    extern com::sun::star::uno::Reference<com::sun::star::io::XInputStream>
    turnToSeekable(
        const com::sun::star::uno::Reference<com::sun::star::io::XInputStream>& xInputStream);

}
<commit_msg>INTEGRATION: CWS changefileheader (1.2.2); FILE MERGED 2008/04/01 16:07:41 thb 1.2.2.3: #i85898# Stripping all external header guards 2008/04/01 13:02:56 thb 1.2.2.2: #i85898# Stripping all external header guards 2008/03/31 13:04:24 rt 1.2.2.1: #i87441# Change license header to LPGL v3.<commit_after>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: bufferedinputstream.hxx,v $
 * $Revision: 1.3 $
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org 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 version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

#include <cppuhelper/weak.hxx>
#include <osl/mutex.hxx>
#include <com/sun/star/io/XInputStream.hpp>
#include <com/sun/star/io/XSeekable.hpp>

namespace chelp {

    class BufferedInputStream
        : public cppu::OWeakObject,
          public com::sun::star::io::XInputStream,
          public com::sun::star::io::XSeekable
    {
    private:

        sal_Int32      m_nBufferLocation;
        sal_Int32      m_nBufferSize;
        sal_Int8      *m_pBuffer;
        osl::Mutex     m_aMutex;

    public:

        BufferedInputStream(
            const com::sun::star::uno::Reference<com::sun::star::io::XInputStream>& xInputStream);

        ~BufferedInputStream();

        virtual com::sun::star::uno::Any SAL_CALL
        queryInterface( const com::sun::star::uno::Type& rType )
            throw( com::sun::star::uno::RuntimeException );

        virtual void SAL_CALL acquire( void ) throw();

        virtual void SAL_CALL release( void ) throw();

        virtual sal_Int32 SAL_CALL readBytes( com::sun::star::uno::Sequence< sal_Int8 >& aData,
                                              sal_Int32 nBytesToRead )
            throw( com::sun::star::io::NotConnectedException,
                   com::sun::star::io::BufferSizeExceededException,
                   com::sun::star::io::IOException,
                   com::sun::star::uno::RuntimeException );

        virtual sal_Int32 SAL_CALL readSomeBytes( com::sun::star::uno::Sequence< sal_Int8 >& aData,
                                                  sal_Int32 nMaxBytesToRead )
            throw( com::sun::star::io::NotConnectedException,
                   com::sun::star::io::BufferSizeExceededException,
                   com::sun::star::io::IOException,
                   com::sun::star::uno::RuntimeException );

        virtual void SAL_CALL skipBytes( sal_Int32 nBytesToSkip )
            throw( com::sun::star::io::NotConnectedException,
                   com::sun::star::io::BufferSizeExceededException,
                   com::sun::star::io::IOException,
                   com::sun::star::uno::RuntimeException );

        virtual sal_Int32 SAL_CALL available( void )
            throw( com::sun::star::io::NotConnectedException,
                   com::sun::star::io::IOException,
                   com::sun::star::uno::RuntimeException );

        virtual void SAL_CALL closeInput( void )
            throw( com::sun::star::io::NotConnectedException,
                   com::sun::star::io::IOException,
                   com::sun::star::uno::RuntimeException );

        virtual void SAL_CALL seek( sal_Int64 location )
            throw( com::sun::star::lang::IllegalArgumentException,
                   com::sun::star::io::IOException,
                   com::sun::star::uno::RuntimeException );

        virtual sal_Int64 SAL_CALL getPosition( void )
            throw( com::sun::star::io::IOException,
                   com::sun::star::uno::RuntimeException );

        virtual sal_Int64 SAL_CALL getLength( void )
            throw( com::sun::star::io::IOException,
                   com::sun::star::uno::RuntimeException );
    };

    extern com::sun::star::uno::Reference<com::sun::star::io::XInputStream>
    turnToSeekable(
        const com::sun::star::uno::Reference<com::sun::star::io::XInputStream>& xInputStream);

}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2014 Andrew Duggan
 * Copyright (C) 2014 Synaptics Inc
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <getopt.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <unistd.h>
#include <time.h>
#include <string>
#include <sstream>

#include "hiddevice.h"
#include "rmi4update.h"

#define VERSION_MAJOR		1
#define VERSION_MINOR		0
#define VERSION_SUBMINOR	0

#define RMI4UPDATE_GETOPTS	"hfd:plv"

void printHelp(const char *prog_name)
{
	fprintf(stdout, "Usage: %s [OPTIONS] FIRMWAREFILE\n", prog_name);
	fprintf(stdout, "\t-h, --help\tPrint this message\n");
	fprintf(stdout, "\t-f, --force\tForce updating firmware even it the image provided is older\n\t\t\tthen the current firmware on the device.\n");
	fprintf(stdout, "\t-d, --device\thidraw device file associated with the device being updated.\n");
	fprintf(stdout, "\t-p, --fw-props\tPrint the firmware properties.\n");
	fprintf(stdout, "\t-l, --lockdown\tPerform lockdown.\n");
	fprintf(stdout, "\t-v, --version\tPrint version number.\n");
}

void printVersion()
{
	fprintf(stdout, "rmi4update version %d.%d.%d\n",
		VERSION_MAJOR, VERSION_MINOR, VERSION_SUBMINOR);
}

int UpdateDevice(FirmwareImage & image, bool force, bool performLockdown, const char * deviceFile)
{
	HIDDevice rmidevice;
	int rc;

	rc = rmidevice.Open(deviceFile);
	if (rc)
		return rc;

	RMI4Update update(rmidevice, image);
	rc = update.UpdateFirmware(force, performLockdown);
	if (rc != UPDATE_SUCCESS)
		return rc;

	return rc;
}

int WriteDeviceNameToFile(const char * file, const char * str)
{
	int fd;
	ssize_t size;

	fd = open(file, O_WRONLY);
	if (fd < 0)
		return UPDATE_FAIL;

	for (;;) {
		size = write(fd, str, 19);
		if (size < 0) {
			if (errno == EINTR)
				continue;

			return UPDATE_FAIL;
		}
		break;
	}

	close(fd);

	return UPDATE_SUCCESS;
}

/*
 * We need to rebind the driver to the device after firmware update because the
 * parameters of the device may have changed in the new firmware and the
 * driver should redo the initialization ensure it is using the new values.
 */
void RebindDriver(const char * hidraw)
{
	int rc;
	ssize_t size;
	char bindFile[PATH_MAX];
	char unbindFile[PATH_MAX];
	char deviceLink[PATH_MAX];
	char driverName[PATH_MAX];
	char driverLink[PATH_MAX];
	char linkBuf[PATH_MAX];
	char hidDeviceString[20];
	int i;

	snprintf(unbindFile, PATH_MAX, "/sys/class/hidraw/%s/device/driver/unbind", hidraw);
	snprintf(deviceLink, PATH_MAX, "/sys/class/hidraw/%s/device", hidraw);

	size = readlink(deviceLink, linkBuf, PATH_MAX);
	if (size < 0) {
		fprintf(stderr, "Failed to find the HID string for this device: %s\n",
			hidraw);
		return;
	}
	linkBuf[size] = 0;

	strncpy(hidDeviceString, StripPath(linkBuf, size), 20);

	snprintf(driverLink, PATH_MAX, "/sys/class/hidraw/%s/device/driver", hidraw);

	size = readlink(driverLink, linkBuf, PATH_MAX);
	if (size < 0) {
		fprintf(stderr, "Failed to find the HID string for this device: %s\n",
			hidraw);
		return;
	}
	linkBuf[size] = 0;

	strncpy(driverName, StripPath(linkBuf, size), PATH_MAX);

	snprintf(bindFile, PATH_MAX, "/sys/bus/hid/drivers/%s/bind", driverName);

	rc = WriteDeviceNameToFile(unbindFile, hidDeviceString);
	if (rc != UPDATE_SUCCESS) {
		fprintf(stderr, "Failed to unbind HID device %s: %s\n",
			hidDeviceString, strerror(errno));
		return;
	}

	for (i = 0;; ++i) {
		struct timespec req;
		struct timespec rem;

		rc = WriteDeviceNameToFile(bindFile, hidDeviceString);
		if (rc == UPDATE_SUCCESS)
			return;

		if (i <= 4)
			break;

		/* device might not be ready yet to bind to */
		req.tv_sec = 0;
		req.tv_nsec = 100 * 1000 * 1000; /* 100 ms */
		for (;;) {
			rc = nanosleep(&req, &rem);
			if (rc < 0) {
				if (errno == EINTR) {
					req = rem;
					continue;
				}
			}
			break;
		}
	}

	fprintf(stderr, "Failed to bind HID device %s: %s\n",
		hidDeviceString, strerror(errno));
}

int GetFirmwareProps(const char * deviceFile, std::string &props)
{
	HIDDevice rmidevice;
	int rc = UPDATE_SUCCESS;
	std::stringstream ss;

	rc = rmidevice.Open(deviceFile);
	if (rc)
		return rc;

	rmidevice.ScanPDT(0x1);
	rmidevice.QueryBasicProperties();

	ss << rmidevice.GetFirmwareVersionMajor() << "."
		<< rmidevice.GetFirmwareVersionMinor() << "."
		<< std::hex << rmidevice.GetFirmwareID();

	if (rmidevice.InBootloader())
		ss << " bootloader";

	props = ss.str();

	return rc;
}

int main(int argc, char **argv)
{
	int rc;
	FirmwareImage image;
	int opt;
	int index;
	char *deviceName = NULL;
	const char *firmwareName = NULL;
	bool force = false;
	static struct option long_options[] = {
		{"help", 0, NULL, 'h'},
		{"force", 0, NULL, 'f'},
		{"device", 1, NULL, 'd'},
		{"fw-props", 0, NULL, 'p'},
		{"lockdown", 0, NULL, 'l'},
		{"version", 0, NULL, 'v'},
		{0, 0, 0, 0},
	};
	struct dirent * devDirEntry;
	DIR * devDir;
	bool printFirmwareProps = false;
	bool performLockdown = false;

	while ((opt = getopt_long(argc, argv, RMI4UPDATE_GETOPTS, long_options, &index)) != -1) {
		switch (opt) {
			case 'h':
				printHelp(argv[0]);
				return 0;
			case 'f':
				force = true;
				break;
			case 'd':
				deviceName = optarg;
				break;
			case 'p':
				printFirmwareProps = true;
				break;
			case 'l':
				performLockdown = true;
				break;
			case 'v':
				printVersion();
				return 0;
			default:
				break;

		}
	}

	if (printFirmwareProps) {
		std::string props;

		if (!deviceName) {
			fprintf(stderr, "Specifiy which device to query\n");
			return 1;
		}
		rc = GetFirmwareProps(deviceName, props);
		if (rc) {
			fprintf(stderr, "Failed to read properties from device: %s\n", update_err_to_string(rc));
			return 1;
		}
		fprintf(stdout, "%s\n", props.c_str());
		return 0;
	}

	if (optind < argc) {
		firmwareName = argv[optind];
	} else {
		printHelp(argv[0]);
		return -1;
	}

	rc = image.Initialize(firmwareName);
	if (rc != UPDATE_SUCCESS) {
		fprintf(stderr, "Failed to initialize the firmware image: %s\n", update_err_to_string(rc));
		return 1;
	}

	if (deviceName) {
		char * rawDevice;
		rc = UpdateDevice(image, force, performLockdown, deviceName);
		if (rc)
			return rc;

		rawDevice = strcasestr(deviceName, "hidraw");
		if (rawDevice)
			RebindDriver(rawDevice);
		return rc;
	} else {
		char rawDevice[PATH_MAX];
		char deviceFile[PATH_MAX];
		bool found = false;

		devDir = opendir("/dev");
		if (!devDir)
			return -1;

		while ((devDirEntry = readdir(devDir)) != NULL) {
			if (strstr(devDirEntry->d_name, "hidraw")) {
				strncpy(rawDevice, devDirEntry->d_name, PATH_MAX);
				snprintf(deviceFile, PATH_MAX, "/dev/%s", devDirEntry->d_name);
				rc = UpdateDevice(image, force, performLockdown, deviceFile);
				if (rc != 0) {
					continue;
				} else {
					found = true;
					RebindDriver(rawDevice);
					break;
				}
			}
		}
		closedir(devDir);

		if (!found)
			return rc;
	}

	return 0;
}
<commit_msg>bump the subminor verison to create a new release with some fixes<commit_after>/*
 * Copyright (C) 2014 Andrew Duggan
 * Copyright (C) 2014 Synaptics Inc
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <getopt.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <unistd.h>
#include <time.h>
#include <string>
#include <sstream>

#include "hiddevice.h"
#include "rmi4update.h"

#define VERSION_MAJOR		1
#define VERSION_MINOR		0
#define VERSION_SUBMINOR	1

#define RMI4UPDATE_GETOPTS	"hfd:plv"

void printHelp(const char *prog_name)
{
	fprintf(stdout, "Usage: %s [OPTIONS] FIRMWAREFILE\n", prog_name);
	fprintf(stdout, "\t-h, --help\tPrint this message\n");
	fprintf(stdout, "\t-f, --force\tForce updating firmware even it the image provided is older\n\t\t\tthen the current firmware on the device.\n");
	fprintf(stdout, "\t-d, --device\thidraw device file associated with the device being updated.\n");
	fprintf(stdout, "\t-p, --fw-props\tPrint the firmware properties.\n");
	fprintf(stdout, "\t-l, --lockdown\tPerform lockdown.\n");
	fprintf(stdout, "\t-v, --version\tPrint version number.\n");
}

void printVersion()
{
	fprintf(stdout, "rmi4update version %d.%d.%d\n",
		VERSION_MAJOR, VERSION_MINOR, VERSION_SUBMINOR);
}

int UpdateDevice(FirmwareImage & image, bool force, bool performLockdown, const char * deviceFile)
{
	HIDDevice rmidevice;
	int rc;

	rc = rmidevice.Open(deviceFile);
	if (rc)
		return rc;

	RMI4Update update(rmidevice, image);
	rc = update.UpdateFirmware(force, performLockdown);
	if (rc != UPDATE_SUCCESS)
		return rc;

	return rc;
}

int WriteDeviceNameToFile(const char * file, const char * str)
{
	int fd;
	ssize_t size;

	fd = open(file, O_WRONLY);
	if (fd < 0)
		return UPDATE_FAIL;

	for (;;) {
		size = write(fd, str, 19);
		if (size < 0) {
			if (errno == EINTR)
				continue;

			return UPDATE_FAIL;
		}
		break;
	}

	close(fd);

	return UPDATE_SUCCESS;
}

/*
 * We need to rebind the driver to the device after firmware update because the
 * parameters of the device may have changed in the new firmware and the
 * driver should redo the initialization ensure it is using the new values.
 */
void RebindDriver(const char * hidraw)
{
	int rc;
	ssize_t size;
	char bindFile[PATH_MAX];
	char unbindFile[PATH_MAX];
	char deviceLink[PATH_MAX];
	char driverName[PATH_MAX];
	char driverLink[PATH_MAX];
	char linkBuf[PATH_MAX];
	char hidDeviceString[20];
	int i;

	snprintf(unbindFile, PATH_MAX, "/sys/class/hidraw/%s/device/driver/unbind", hidraw);
	snprintf(deviceLink, PATH_MAX, "/sys/class/hidraw/%s/device", hidraw);

	size = readlink(deviceLink, linkBuf, PATH_MAX);
	if (size < 0) {
		fprintf(stderr, "Failed to find the HID string for this device: %s\n",
			hidraw);
		return;
	}
	linkBuf[size] = 0;

	strncpy(hidDeviceString, StripPath(linkBuf, size), 20);

	snprintf(driverLink, PATH_MAX, "/sys/class/hidraw/%s/device/driver", hidraw);

	size = readlink(driverLink, linkBuf, PATH_MAX);
	if (size < 0) {
		fprintf(stderr, "Failed to find the HID string for this device: %s\n",
			hidraw);
		return;
	}
	linkBuf[size] = 0;

	strncpy(driverName, StripPath(linkBuf, size), PATH_MAX);

	snprintf(bindFile, PATH_MAX, "/sys/bus/hid/drivers/%s/bind", driverName);

	rc = WriteDeviceNameToFile(unbindFile, hidDeviceString);
	if (rc != UPDATE_SUCCESS) {
		fprintf(stderr, "Failed to unbind HID device %s: %s\n",
			hidDeviceString, strerror(errno));
		return;
	}

	for (i = 0;; ++i) {
		struct timespec req;
		struct timespec rem;

		rc = WriteDeviceNameToFile(bindFile, hidDeviceString);
		if (rc == UPDATE_SUCCESS)
			return;

		if (i <= 4)
			break;

		/* device might not be ready yet to bind to */
		req.tv_sec = 0;
		req.tv_nsec = 100 * 1000 * 1000; /* 100 ms */
		for (;;) {
			rc = nanosleep(&req, &rem);
			if (rc < 0) {
				if (errno == EINTR) {
					req = rem;
					continue;
				}
			}
			break;
		}
	}

	fprintf(stderr, "Failed to bind HID device %s: %s\n",
		hidDeviceString, strerror(errno));
}

int GetFirmwareProps(const char * deviceFile, std::string &props)
{
	HIDDevice rmidevice;
	int rc = UPDATE_SUCCESS;
	std::stringstream ss;

	rc = rmidevice.Open(deviceFile);
	if (rc)
		return rc;

	rmidevice.ScanPDT(0x1);
	rmidevice.QueryBasicProperties();

	ss << rmidevice.GetFirmwareVersionMajor() << "."
		<< rmidevice.GetFirmwareVersionMinor() << "."
		<< std::hex << rmidevice.GetFirmwareID();

	if (rmidevice.InBootloader())
		ss << " bootloader";

	props = ss.str();

	return rc;
}

int main(int argc, char **argv)
{
	int rc;
	FirmwareImage image;
	int opt;
	int index;
	char *deviceName = NULL;
	const char *firmwareName = NULL;
	bool force = false;
	static struct option long_options[] = {
		{"help", 0, NULL, 'h'},
		{"force", 0, NULL, 'f'},
		{"device", 1, NULL, 'd'},
		{"fw-props", 0, NULL, 'p'},
		{"lockdown", 0, NULL, 'l'},
		{"version", 0, NULL, 'v'},
		{0, 0, 0, 0},
	};
	struct dirent * devDirEntry;
	DIR * devDir;
	bool printFirmwareProps = false;
	bool performLockdown = false;

	while ((opt = getopt_long(argc, argv, RMI4UPDATE_GETOPTS, long_options, &index)) != -1) {
		switch (opt) {
			case 'h':
				printHelp(argv[0]);
				return 0;
			case 'f':
				force = true;
				break;
			case 'd':
				deviceName = optarg;
				break;
			case 'p':
				printFirmwareProps = true;
				break;
			case 'l':
				performLockdown = true;
				break;
			case 'v':
				printVersion();
				return 0;
			default:
				break;

		}
	}

	if (printFirmwareProps) {
		std::string props;

		if (!deviceName) {
			fprintf(stderr, "Specifiy which device to query\n");
			return 1;
		}
		rc = GetFirmwareProps(deviceName, props);
		if (rc) {
			fprintf(stderr, "Failed to read properties from device: %s\n", update_err_to_string(rc));
			return 1;
		}
		fprintf(stdout, "%s\n", props.c_str());
		return 0;
	}

	if (optind < argc) {
		firmwareName = argv[optind];
	} else {
		printHelp(argv[0]);
		return -1;
	}

	rc = image.Initialize(firmwareName);
	if (rc != UPDATE_SUCCESS) {
		fprintf(stderr, "Failed to initialize the firmware image: %s\n", update_err_to_string(rc));
		return 1;
	}

	if (deviceName) {
		char * rawDevice;
		rc = UpdateDevice(image, force, performLockdown, deviceName);
		if (rc)
			return rc;

		rawDevice = strcasestr(deviceName, "hidraw");
		if (rawDevice)
			RebindDriver(rawDevice);
		return rc;
	} else {
		char rawDevice[PATH_MAX];
		char deviceFile[PATH_MAX];
		bool found = false;

		devDir = opendir("/dev");
		if (!devDir)
			return -1;

		while ((devDirEntry = readdir(devDir)) != NULL) {
			if (strstr(devDirEntry->d_name, "hidraw")) {
				strncpy(rawDevice, devDirEntry->d_name, PATH_MAX);
				snprintf(deviceFile, PATH_MAX, "/dev/%s", devDirEntry->d_name);
				rc = UpdateDevice(image, force, performLockdown, deviceFile);
				if (rc != 0) {
					continue;
				} else {
					found = true;
					RebindDriver(rawDevice);
					break;
				}
			}
		}
		closedir(devDir);

		if (!found)
			return rc;
	}

	return 0;
}
<|endoftext|>


<commit_before>/*=====================================================================

QGroundControl Open Source Ground Control Station

(c) 2009, 2010 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>

This file is part of the QGROUNDCONTROL project

    QGROUNDCONTROL is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    QGROUNDCONTROL 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.

======================================================================*/

/**
 * @file
 *   @brief Implementation of CommConfigurationWindow
 *
 *   @author Lorenz Meier <mavteam@student.ethz.ch>
 *
 */

#include <QDebug>

#include <QDir>
#include <QFileInfoList>
#include <QBoxLayout>
#include <QWidget>

#include "CommConfigurationWindow.h"
#include "SerialConfigurationWindow.h"
#include "SerialLink.h"
#include "UDPLink.h"
#include "MAVLinkSimulationLink.h"
#ifdef XBEELINK
#include "XbeeLink.h"
#include "XbeeConfigurationWindow.h"
#endif // XBEELINK
#ifdef OPAL_RT
#include "OpalLink.h"
#include "OpalLinkConfigurationWindow.h"
#endif
#include "MAVLinkProtocol.h"
#include "MAVLinkSettingsWidget.h"
#include "QGCUDPLinkConfiguration.h"
#include "LinkManager.h"

CommConfigurationWindow::CommConfigurationWindow(LinkInterface* link, ProtocolInterface* protocol, QWidget *parent, Qt::WindowFlags flags) : QWidget(parent, flags)
{
    this->link = link;

    // Setup the user interface according to link type
    ui.setupUi(this);

    // add link types
    ui.linkType->addItem(tr("Serial"), QGC_LINK_SERIAL);
    ui.linkType->addItem(tr("UDP"), QGC_LINK_UDP);
    ui.linkType->addItem(tr("Simulation"), QGC_LINK_SIMULATION);
    ui.linkType->addItem(tr("Opal-RT Link"), QGC_LINK_OPAL);
#ifdef XBEELINK
	ui.linkType->addItem(tr("Xbee API"),QGC_LINK_XBEE);
#endif // XBEELINK
    ui.linkType->setEditable(false);
    //ui.linkType->setEnabled(false);

    ui.connectionType->addItem("MAVLink", QGC_PROTOCOL_MAVLINK);
    //ui.connectionType->addItem("GPS NMEA", QGC_PROTOCOL_NMEA);

    // Create action to open this menu
    // Create configuration action for this link
    // Connect the current UAS
    action = new QAction(QIcon(":/images/devices/network-wireless.svg"), "", this);
    LinkManager::instance()->add(link);
    action->setData(LinkManager::instance()->getLinks().indexOf(link));
    action->setEnabled(true);
    action->setVisible(true);
    setLinkName(link->getName());
    connect(action, SIGNAL(triggered()), this, SLOT(show()));

    // Make sure that a change in the link name will be reflected in the UI
    connect(link, SIGNAL(nameChanged(QString)), this, SLOT(setLinkName(QString)));

    // Setup user actions and link notifications
    connect(ui.connectButton, SIGNAL(clicked()), this, SLOT(setConnection()));
    connect(ui.closeButton, SIGNAL(clicked()), this->window(), SLOT(close()));
    connect(ui.deleteButton, SIGNAL(clicked()), this, SLOT(remove()));

    connect(this->link, SIGNAL(connected(bool)), this, SLOT(connectionState(bool)));

#ifdef XBEELINK
	connect(ui.linkType,SIGNAL(currentIndexChanged(int)),this,SLOT(setLinkType(int)));
#endif // XBEELINK

    // Fill in the current data
    if(this->link->isConnected()) ui.connectButton->setChecked(true);
    //connect(this->link, SIGNAL(connected(bool)), ui.connectButton, SLOT(setChecked(bool)));

    if(this->link->isConnected()) {
        ui.connectionStatusLabel->setText(tr("Connected"));

        // TODO Deactivate all settings to force user to manually disconnect first
    } else {
        ui.connectionStatusLabel->setText(tr("Disconnected"));
    }

    // TODO Move these calls to each link so that dynamic casts vanish

    // Open details pane for serial link if necessary
    SerialLink* serial = dynamic_cast<SerialLink*>(link);
    if(serial != 0) {
        QWidget* conf = new SerialConfigurationWindow(serial, this);
        //QBoxLayout* layout = new QBoxLayout(QBoxLayout::LeftToRight, ui.linkGroupBox);
        //layout->addWidget(conf);
        ui.linkScrollArea->setWidget(conf);
        //ui.linkScrollArea->setLayout(layout);
        ui.linkGroupBox->setTitle(tr("Serial Link"));
        ui.linkType->setCurrentIndex(0);
        //ui.linkGroupBox->setTitle(link->getName());
        //connect(link, SIGNAL(nameChanged(QString)), ui.linkGroupBox, SLOT(setTitle(QString)));
    }
    UDPLink* udp = dynamic_cast<UDPLink*>(link);
    if (udp != 0) {
        QWidget* conf = new QGCUDPLinkConfiguration(udp, this);
        //QBoxLayout* layout = new QBoxLayout(QBoxLayout::LeftToRight, ui.linkGroupBox);
        //layout->addWidget(conf);
        //ui.linkGroupBox->setLayout(layout);
        ui.linkScrollArea->setWidget(conf);
        ui.linkGroupBox->setTitle(tr("UDP Link"));
        ui.linkType->setCurrentIndex(1);
    }
    MAVLinkSimulationLink* sim = dynamic_cast<MAVLinkSimulationLink*>(link);
    if (sim != 0) {
        ui.linkType->setCurrentIndex(2);
        ui.linkGroupBox->setTitle(tr("MAVLink Simulation Link"));
    }
#ifdef OPAL_RT
    OpalLink* opal = dynamic_cast<OpalLink*>(link);
    if (opal != 0) {
        QWidget* conf = new OpalLinkConfigurationWindow(opal, this);
        QBoxLayout* layout = new QBoxLayout(QBoxLayout::LeftToRight, ui.linkGroupBox);
        layout->addWidget(conf);
        ui.linkGroupBox->setLayout(layout);
        ui.linkType->setCurrentIndex(3);
        ui.linkGroupBox->setTitle(tr("Opal-RT Link"));
    }
#endif
#ifdef XBEELINK
	XbeeLink* xbee = dynamic_cast<XbeeLink*>(link); // new Konrad
	if(xbee != 0)
	{
		QWidget* conf = new XbeeConfigurationWindow(xbee,this);
		ui.linkScrollArea->setWidget(conf);
		ui.linkGroupBox->setTitle(tr("Xbee Link"));
		//ui.linkType->setCurrentIndex(4);
	}
#endif // XBEELINK
    if (serial == 0 && udp == 0 && sim == 0
#ifdef OPAL_RT
            && opal == 0
#endif
#ifdef XBEELINK
			&& xbee == 0
#endif // XBEELINK
       ) {
        qDebug() << "Link is NOT a known link, can't open configuration window";
    }

    // Open details pane for MAVLink if necessary
    MAVLinkProtocol* mavlink = dynamic_cast<MAVLinkProtocol*>(protocol);
    if (mavlink != 0) {
        QWidget* conf = new MAVLinkSettingsWidget(mavlink, this);
        //QBoxLayout* layout = new QBoxLayout(QBoxLayout::LeftToRight, ui.protocolGroupBox);
        //layout->addWidget(conf);
        //ui.protocolGroupBox->setLayout(layout);
        ui.protocolScrollArea->setWidget(conf);
        ui.protocolGroupBox->setTitle(protocol->getName()+" (Global Settings)");
    } else {
        qDebug() << "Protocol is NOT MAVLink, can't open configuration window";
    }

    // Open details for UDP link if necessary
    // TODO

    // Display the widget
    this->window()->setWindowTitle(tr("Settings for ") + this->link->getName());
    this->hide();
}

CommConfigurationWindow::~CommConfigurationWindow()
{

}

QAction* CommConfigurationWindow::getAction()
{
    return action;
}

void CommConfigurationWindow::setLinkType(int linktype)
{
#ifdef XBEELINK
    // Adjust the form layout per link type
	if(ui.linkScrollArea->widget())
	{
		delete ui.linkScrollArea->widget();
	}
	switch(linktype)
	{
		case 4:
			{
				XbeeLink *xbee = new XbeeLink();
				link = xbee;
				LinkManager::instance()->add(link);
				QWidget* conf = new XbeeConfigurationWindow(link);
				ui.linkScrollArea->setWidget(conf);
				ui.linkGroupBox->setTitle(tr("Serial Link"));
				break;
			}
		case 0:
			{
				SerialLink *serial = new SerialLink();
				link = serial;
				LinkManager::instance()->add(link);
				QWidget* conf = new SerialConfigurationWindow(link, this);
				ui.linkScrollArea->setWidget(conf);
				ui.linkGroupBox->setTitle(tr("Serial Link"));
				break;
			}
		default:
			break;
	}
#endif // XBEELINK
}

void CommConfigurationWindow::setProtocol(int protocol)
{
    qDebug() << "Changing to protocol" << protocol;
}

void CommConfigurationWindow::setConnection()
{
    if(!link->isConnected()) {
        link->connect();
    } else {
        link->disconnect();
    }
}

void CommConfigurationWindow::setLinkName(QString name)
{
    action->setText(tr("%1 Settings").arg(name));
    action->setStatusTip(tr("Adjust setting for link %1").arg(name));
    this->window()->setWindowTitle(tr("Settings for %1").arg(name));
}

void CommConfigurationWindow::remove()
{
    if(action) delete action; //delete action first since it has a pointer to link
    action=NULL;

    if(link) {
        LinkManager::instance()->removeLink(link); //remove link from LinkManager list
        link->disconnect(); //disconnect port, and also calls terminate() to stop the thread
        if (link->isRunning()) link->terminate(); // terminate() the serial thread just in case it is still running
        link->wait(); // wait() until thread is stoped before deleting
        link->deleteLater();
    }
    link=NULL;

    this->window()->close();
    this->deleteLater();
}

void CommConfigurationWindow::connectionState(bool connect)
{
    ui.connectButton->setChecked(connect);
    if(connect) {
        ui.connectionStatusLabel->setText(tr("Connected"));
        ui.connectButton->setText(tr("Disconnect"));
    } else {
        ui.connectionStatusLabel->setText(tr("Disconnected"));
        ui.connectButton->setText(tr("Connect"));
    }
}
<commit_msg>changed Xbee pop up<commit_after>/*=====================================================================

QGroundControl Open Source Ground Control Station

(c) 2009, 2010 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>

This file is part of the QGROUNDCONTROL project

    QGROUNDCONTROL is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    QGROUNDCONTROL 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 General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.

======================================================================*/

/**
 * @file
 *   @brief Implementation of CommConfigurationWindow
 *
 *   @author Lorenz Meier <mavteam@student.ethz.ch>
 *
 */

#include <QDebug>

#include <QDir>
#include <QFileInfoList>
#include <QBoxLayout>
#include <QWidget>

#include "CommConfigurationWindow.h"
#include "SerialConfigurationWindow.h"
#include "SerialLink.h"
#include "UDPLink.h"
#include "MAVLinkSimulationLink.h"
#ifdef XBEELINK
#include "XbeeLink.h"
#include "XbeeConfigurationWindow.h"
#endif // XBEELINK
#ifdef OPAL_RT
#include "OpalLink.h"
#include "OpalLinkConfigurationWindow.h"
#endif
#include "MAVLinkProtocol.h"
#include "MAVLinkSettingsWidget.h"
#include "QGCUDPLinkConfiguration.h"
#include "LinkManager.h"
#include "MainWindow.h"

CommConfigurationWindow::CommConfigurationWindow(LinkInterface* link, ProtocolInterface* protocol, QWidget *parent, Qt::WindowFlags flags) : QWidget(parent, flags)
{
    this->link = link;

    // Setup the user interface according to link type
    ui.setupUi(this);

    // add link types
    ui.linkType->addItem(tr("Serial"), QGC_LINK_SERIAL);
    ui.linkType->addItem(tr("UDP"), QGC_LINK_UDP);
    ui.linkType->addItem(tr("Simulation"), QGC_LINK_SIMULATION);
    ui.linkType->addItem(tr("Opal-RT Link"), QGC_LINK_OPAL);
#ifdef XBEELINK
	ui.linkType->addItem(tr("Xbee API"),QGC_LINK_XBEE);
#endif // XBEELINK
    ui.linkType->setEditable(false);
    //ui.linkType->setEnabled(false);

    ui.connectionType->addItem("MAVLink", QGC_PROTOCOL_MAVLINK);
    //ui.connectionType->addItem("GPS NMEA", QGC_PROTOCOL_NMEA);

    // Create action to open this menu
    // Create configuration action for this link
    // Connect the current UAS
    action = new QAction(QIcon(":/images/devices/network-wireless.svg"), "", this);
    LinkManager::instance()->add(link);
    action->setData(LinkManager::instance()->getLinks().indexOf(link));
    action->setEnabled(true);
    action->setVisible(true);
    setLinkName(link->getName());
    connect(action, SIGNAL(triggered()), this, SLOT(show()));

    // Make sure that a change in the link name will be reflected in the UI
    connect(link, SIGNAL(nameChanged(QString)), this, SLOT(setLinkName(QString)));

    // Setup user actions and link notifications
    connect(ui.connectButton, SIGNAL(clicked()), this, SLOT(setConnection()));
    connect(ui.closeButton, SIGNAL(clicked()), this->window(), SLOT(close()));
    connect(ui.deleteButton, SIGNAL(clicked()), this, SLOT(remove()));

    connect(this->link, SIGNAL(connected(bool)), this, SLOT(connectionState(bool)));

    // Fill in the current data
    if(this->link->isConnected()) ui.connectButton->setChecked(true);
    //connect(this->link, SIGNAL(connected(bool)), ui.connectButton, SLOT(setChecked(bool)));

    if(this->link->isConnected()) {
        ui.connectionStatusLabel->setText(tr("Connected"));

        // TODO Deactivate all settings to force user to manually disconnect first
    } else {
        ui.connectionStatusLabel->setText(tr("Disconnected"));
    }

    // TODO Move these calls to each link so that dynamic casts vanish

    // Open details pane for serial link if necessary
    SerialLink* serial = dynamic_cast<SerialLink*>(link);
    if(serial != 0) {
        QWidget* conf = new SerialConfigurationWindow(serial, this);
        //QBoxLayout* layout = new QBoxLayout(QBoxLayout::LeftToRight, ui.linkGroupBox);
        //layout->addWidget(conf);
        ui.linkScrollArea->setWidget(conf);
        //ui.linkScrollArea->setLayout(layout);
        ui.linkGroupBox->setTitle(tr("Serial Link"));
        ui.linkType->setCurrentIndex(0);
        //ui.linkGroupBox->setTitle(link->getName());
        //connect(link, SIGNAL(nameChanged(QString)), ui.linkGroupBox, SLOT(setTitle(QString)));
    }
    UDPLink* udp = dynamic_cast<UDPLink*>(link);
    if (udp != 0) {
        QWidget* conf = new QGCUDPLinkConfiguration(udp, this);
        //QBoxLayout* layout = new QBoxLayout(QBoxLayout::LeftToRight, ui.linkGroupBox);
        //layout->addWidget(conf);
        //ui.linkGroupBox->setLayout(layout);
        ui.linkScrollArea->setWidget(conf);
        ui.linkGroupBox->setTitle(tr("UDP Link"));
        ui.linkType->setCurrentIndex(1);
    }
    MAVLinkSimulationLink* sim = dynamic_cast<MAVLinkSimulationLink*>(link);
    if (sim != 0) {
        ui.linkType->setCurrentIndex(2);
        ui.linkGroupBox->setTitle(tr("MAVLink Simulation Link"));
    }
#ifdef OPAL_RT
    OpalLink* opal = dynamic_cast<OpalLink*>(link);
    if (opal != 0) {
        QWidget* conf = new OpalLinkConfigurationWindow(opal, this);
        QBoxLayout* layout = new QBoxLayout(QBoxLayout::LeftToRight, ui.linkGroupBox);
        layout->addWidget(conf);
        ui.linkGroupBox->setLayout(layout);
        ui.linkType->setCurrentIndex(3);
        ui.linkGroupBox->setTitle(tr("Opal-RT Link"));
    }
#endif
#ifdef XBEELINK
	XbeeLink* xbee = dynamic_cast<XbeeLink*>(link); // new Konrad
	if(xbee != 0)
	{
		QWidget* conf = new XbeeConfigurationWindow(xbee,this);
		ui.linkScrollArea->setWidget(conf);
		ui.linkGroupBox->setTitle(tr("Xbee Link"));
		ui.linkType->setCurrentIndex(4);
	}
#endif // XBEELINK
    if (serial == 0 && udp == 0 && sim == 0
#ifdef OPAL_RT
            && opal == 0
#endif
#ifdef XBEELINK
			&& xbee == 0
#endif // XBEELINK
       ) {
        qDebug() << "Link is NOT a known link, can't open configuration window";
    }

#ifdef XBEELINK
	connect(ui.linkType,SIGNAL(currentIndexChanged(int)),this,SLOT(setLinkType(int)));
#endif // XBEELINK

    // Open details pane for MAVLink if necessary
    MAVLinkProtocol* mavlink = dynamic_cast<MAVLinkProtocol*>(protocol);
    if (mavlink != 0) {
        QWidget* conf = new MAVLinkSettingsWidget(mavlink, this);
        //QBoxLayout* layout = new QBoxLayout(QBoxLayout::LeftToRight, ui.protocolGroupBox);
        //layout->addWidget(conf);
        //ui.protocolGroupBox->setLayout(layout);
        ui.protocolScrollArea->setWidget(conf);
        ui.protocolGroupBox->setTitle(protocol->getName()+" (Global Settings)");
    } else {
        qDebug() << "Protocol is NOT MAVLink, can't open configuration window";
    }

    // Open details for UDP link if necessary
    // TODO

    // Display the widget
    this->window()->setWindowTitle(tr("Settings for ") + this->link->getName());
    this->hide();
}

CommConfigurationWindow::~CommConfigurationWindow()
{

}

QAction* CommConfigurationWindow::getAction()
{
    return action;
}

void CommConfigurationWindow::setLinkType(int linktype)
{
#ifdef XBEELINK
	// close old configuration window
	this->window()->close();

	switch(linktype)
	{
		case 4:
			{
				XbeeLink *xbee = new XbeeLink();
				MainWindow::instance()->addLink(xbee);
				break;
			}
		case 0:
			{
				SerialLink *serial = new SerialLink();
				MainWindow::instance()->addLink(serial);
				break;
			}
		default:
			{
				MainWindow::instance()->addLink();
				break;
			}
	}
#endif // XBEELINK
}

void CommConfigurationWindow::setProtocol(int protocol)
{
    qDebug() << "Changing to protocol" << protocol;
}

void CommConfigurationWindow::setConnection()
{
    if(!link->isConnected()) {
        link->connect();
    } else {
        link->disconnect();
    }
}

void CommConfigurationWindow::setLinkName(QString name)
{
    action->setText(tr("%1 Settings").arg(name));
    action->setStatusTip(tr("Adjust setting for link %1").arg(name));
    this->window()->setWindowTitle(tr("Settings for %1").arg(name));
}

void CommConfigurationWindow::remove()
{
    if(action) delete action; //delete action first since it has a pointer to link
    action=NULL;

    if(link) {
        LinkManager::instance()->removeLink(link); //remove link from LinkManager list
        link->disconnect(); //disconnect port, and also calls terminate() to stop the thread
        if (link->isRunning()) link->terminate(); // terminate() the serial thread just in case it is still running
        link->wait(); // wait() until thread is stoped before deleting
        link->deleteLater();
    }
    link=NULL;

    this->window()->close();
    this->deleteLater();
}

void CommConfigurationWindow::connectionState(bool connect)
{
    ui.connectButton->setChecked(connect);
    if(connect) {
        ui.connectionStatusLabel->setText(tr("Connected"));
        ui.connectButton->setText(tr("Disconnect"));
    } else {
        ui.connectionStatusLabel->setText(tr("Disconnected"));
        ui.connectButton->setText(tr("Connect"));
    }
}
<|endoftext|>


<commit_before>// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License

#ifndef __PROCESS_POSIX_SUBPROCESS_HPP__
#define __PROCESS_POSIX_SUBPROCESS_HPP__

#include <sys/types.h>

#include <string>

#include <glog/logging.h>

#include <process/subprocess.hpp>

#include <stout/error.hpp>
#include <stout/foreach.hpp>
#include <stout/nothing.hpp>
#include <stout/lambda.hpp>
#include <stout/none.hpp>
#include <stout/option.hpp>
#include <stout/os.hpp>
#include <stout/try.hpp>
#include <stout/unreachable.hpp>

#include <stout/os/close.hpp>
#include <stout/os/environment.hpp>
#include <stout/os/fcntl.hpp>
#include <stout/os/signals.hpp>
#include <stout/os/strerror.hpp>

using std::map;
using std::string;
using std::vector;

namespace process {

using InputFileDescriptors = Subprocess::IO::InputFileDescriptors;
using OutputFileDescriptors = Subprocess::IO::OutputFileDescriptors;

namespace internal {

// This function will invoke `os::close` on all specified file
// descriptors that are valid (i.e., not `None` and >= 0).
inline void close(
    const InputFileDescriptors& stdinfds,
    const OutputFileDescriptors& stdoutfds,
    const OutputFileDescriptors& stderrfds)
{
  int fds[6] = {
    stdinfds.read, stdinfds.write.getOrElse(-1),
    stdoutfds.read.getOrElse(-1), stdoutfds.write,
    stderrfds.read.getOrElse(-1), stderrfds.write
  };

  foreach (int fd, fds) {
    if (fd >= 0) {
      os::close(fd);
    }
  }
}

// This function will invoke `os::cloexec` on all specified file
// descriptors that are valid (i.e., not `None` and >= 0).
inline Try<Nothing> cloexec(
    const InputFileDescriptors& stdinfds,
    const OutputFileDescriptors& stdoutfds,
    const OutputFileDescriptors& stderrfds)
{
  int fds[6] = {
    stdinfds.read, stdinfds.write.getOrElse(-1),
    stdoutfds.read.getOrElse(-1), stdoutfds.write,
    stderrfds.read.getOrElse(-1), stderrfds.write
  };

  foreach (int fd, fds) {
    if (fd >= 0) {
      Try<Nothing> cloexec = os::cloexec(fd);
      if (cloexec.isError()) {
        return Error(cloexec.error());
      }
    }
  }

  return Nothing();
}

inline pid_t defaultClone(const lambda::function<int()>& func)
{
  pid_t pid = ::fork();
  if (pid == -1) {
    return -1;
  } else if (pid == 0) {
    // Child.
    ::exit(func());
    UNREACHABLE();
  } else {
    // Parent.
    return pid;
  }
}

inline void signalHandler(int signal)
{
  // Send SIGKILL to every process in the process group of the
  // calling process.
  kill(0, SIGKILL);
  abort();
}

// Creates a seperate watchdog process to monitor the child process and
// kill it in case the parent process dies.
//
// NOTE: This function needs to be async signal safe. In fact,
// all the library functions we used in this function are async
// signal safe.
inline int watchdogProcess()
{
#ifdef __linux__
  // Send SIGTERM to the current process if the parent (i.e., the
  // slave) exits.
  // NOTE:: This function should always succeed because we are passing
  // in a valid signal.
  prctl(PR_SET_PDEATHSIG, SIGTERM);

  // Put the current process into a separate process group so that
  // we can kill it and all its children easily.
  if (setpgid(0, 0) != 0) {
    abort();
  }

  // Install a SIGTERM handler which will kill the current process
  // group. Since we already setup the death signal above, the
  // signal handler will be triggered when the parent (e.g., the
  // slave) exits.
  if (os::signals::install(SIGTERM, &signalHandler) != 0) {
    abort();
  }

  pid_t pid = fork();
  if (pid == -1) {
    abort();
  } else if (pid == 0) {
    // Child. This is the process that is going to exec the
    // process if zero is returned.

    // We setup death signal for the process as well in case
    // someone, though unlikely, accidentally kill the parent of
    // this process (the bookkeeping process).
    prctl(PR_SET_PDEATHSIG, SIGKILL);

    // NOTE: We don't need to clear the signal handler explicitly
    // because the subsequent 'exec' will clear them.
    return 0;
  } else {
    // Parent. This is the bookkeeping process which will wait for
    // the child process to finish.

    // Close the files to prevent interference on the communication
    // between the slave and the child process.
    close(STDIN_FILENO);
    close(STDOUT_FILENO);
    close(STDERR_FILENO);

    // Block until the child process finishes.
    int status = 0;
    if (waitpid(pid, &status, 0) == -1) {
      abort();
    }

    // Forward the exit status if the child process exits normally.
    if (WIFEXITED(status)) {
      _exit(WEXITSTATUS(status));
    }

    abort();
    UNREACHABLE();
  }
#endif
  return 0;
}

// The main entry of the child process.
//
// NOTE: This function has to be async signal safe.
inline int childMain(
    const string& path,
    char** argv,
    char** envp,
    const Setsid set_sid,
    const InputFileDescriptors& stdinfds,
    const OutputFileDescriptors& stdoutfds,
    const OutputFileDescriptors& stderrfds,
    bool blocking,
    int pipes[2],
    const Option<string>& working_directory,
    const Watchdog watchdog)
{
  // Close parent's end of the pipes.
  if (stdinfds.write.isSome()) {
    ::close(stdinfds.write.get());
  }
  if (stdoutfds.read.isSome()) {
    ::close(stdoutfds.read.get());
  }
  if (stderrfds.read.isSome()) {
    ::close(stderrfds.read.get());
  }

  // Currently we will block the child's execution of the new process
  // until all the parent hooks (if any) have executed.
  if (blocking) {
    ::close(pipes[1]);
  }

  // Redirect I/O for stdin/stdout/stderr.
  while (::dup2(stdinfds.read, STDIN_FILENO) == -1 && errno == EINTR);
  while (::dup2(stdoutfds.write, STDOUT_FILENO) == -1 && errno == EINTR);
  while (::dup2(stderrfds.write, STDERR_FILENO) == -1 && errno == EINTR);

  // Close the copies. We need to make sure that we do not close the
  // file descriptor assigned to stdin/stdout/stderr in case the
  // parent has closed stdin/stdout/stderr when calling this
  // function (in that case, a dup'ed file descriptor may have the
  // same file descriptor number as stdin/stdout/stderr).
  if (stdinfds.read != STDIN_FILENO &&
      stdinfds.read != STDOUT_FILENO &&
      stdinfds.read != STDERR_FILENO) {
    ::close(stdinfds.read);
  }
  if (stdoutfds.write != STDIN_FILENO &&
      stdoutfds.write != STDOUT_FILENO &&
      stdoutfds.write != STDERR_FILENO) {
    ::close(stdoutfds.write);
  }
  if (stderrfds.write != STDIN_FILENO &&
      stderrfds.write != STDOUT_FILENO &&
      stderrfds.write != STDERR_FILENO) {
    ::close(stderrfds.write);
  }

  if (blocking) {
    // Do a blocking read on the pipe until the parent signals us to
    // continue.
    char dummy;
    ssize_t length;
    while ((length = ::read(pipes[0], &dummy, sizeof(dummy))) == -1 &&
          errno == EINTR);

    if (length != sizeof(dummy)) {
      ABORT("Failed to synchronize with parent");
    }

    // Now close the pipe as we don't need it anymore.
    ::close(pipes[0]);
  }

  // Move to a different session (and new process group) so we're
  // independent from the caller's session (otherwise children will
  // receive SIGHUP if the slave exits).
  if (set_sid == SETSID) {
    // POSIX guarantees a forked child's pid does not match any existing
    // process group id so only a single `setsid()` is required and the
    // session id will be the pid.
    if (::setsid() == -1) {
      ABORT("Failed to put child in a new session");
    }
  }

  if (working_directory.isSome()) {
    if (::chdir(working_directory->c_str()) == -1) {
      ABORT("Failed to change directory");
    }
  }

  // If the child process should die together with its parent we spawn a
  // separate watchdog process which kills the child when the parent dies.
  //
  // NOTE: The watchdog process sets the process group id in order for it and
  // its child processes to be killed together. We should not (re)set the sid
  // after this.
  if (watchdog == MONITOR) {
    watchdogProcess();
  }

  os::execvpe(path.c_str(), argv, envp);

  ABORT("Failed to os::execvpe on path '" + path + "': " + os::strerror(errno));
}

inline Try<pid_t> cloneChild(
    const string& path,
    vector<string> argv,
    const Setsid set_sid,
    const Option<map<string, string>>& environment,
    const Option<lambda::function<
        pid_t(const lambda::function<int()>&)>>& _clone,
    const vector<Subprocess::Hook>& parent_hooks,
    const Option<string>& working_directory,
    const Watchdog watchdog,
    const InputFileDescriptors stdinfds,
    const OutputFileDescriptors stdoutfds,
    const OutputFileDescriptors stderrfds)
{
  // The real arguments that will be passed to 'os::execvpe'. We need
  // to construct them here before doing the clone as it might not be
  // async signal safe to perform the memory allocation.
  char** _argv = new char*[argv.size() + 1];
  for (int i = 0; i < argv.size(); i++) {
    _argv[i] = (char*) argv[i].c_str();
  }
  _argv[argv.size()] = NULL;

  // Like above, we need to construct the environment that we'll pass
  // to 'os::execvpe' as it might not be async-safe to perform the
  // memory allocations.
  char** envp = os::raw::environment();

  if (environment.isSome()) {
    // NOTE: We add 1 to the size for a NULL terminator.
    envp = new char*[environment.get().size() + 1];

    size_t index = 0;
    foreachpair (const string& key, const string& value, environment.get()) {
      string entry = key + "=" + value;
      envp[index] = new char[entry.size() + 1];
      strncpy(envp[index], entry.c_str(), entry.size() + 1);
      ++index;
    }

    envp[index] = NULL;
  }

  // Determine the function to clone the child process. If the user
  // does not specify the clone function, we will use the default.
  lambda::function<pid_t(const lambda::function<int()>&)> clone =
    (_clone.isSome() ? _clone.get() : defaultClone);

  // Currently we will block the child's execution of the new process
  // until all the `parent_hooks` (if any) have executed.
  int pipes[2];
  const bool blocking = !parent_hooks.empty();

  if (blocking) {
    // We assume this should not fail under reasonable conditions so we
    // use CHECK.
    CHECK_EQ(0, ::pipe(pipes));
  }

  // Now, clone the child process.
  pid_t pid = clone(lambda::bind(
      &childMain,
      path,
      _argv,
      envp,
      set_sid,
      stdinfds,
      stdoutfds,
      stderrfds,
      blocking,
      pipes,
      working_directory,
      watchdog));

  delete[] _argv;

  // Need to delete 'envp' if we had environment variables passed to
  // us and we needed to allocate the space.
  if (environment.isSome()) {
    CHECK_NE(os::raw::environment(), envp);

    // We ignore the last 'envp' entry since it is NULL.
    for (size_t index = 0; index < environment->size(); index++) {
      delete[] envp[index];
    }

    delete[] envp;
  }

  if (pid == -1) {
    // Save the errno as 'close' below might overwrite it.
    ErrnoError error("Failed to clone");
    internal::close(stdinfds, stdoutfds, stderrfds);

    if (blocking) {
      os::close(pipes[0]);
      os::close(pipes[1]);
    }

    return error;
  }

  if (blocking) {
    os::close(pipes[0]);

    // Run the parent hooks.
    foreach (const Subprocess::Hook& hook, parent_hooks) {
      Try<Nothing> callback = hook.parent_callback(pid);

      // If the hook callback fails, we shouldn't proceed with the
      // execution and hence the child process should be killed.
      if (callback.isError()) {
        LOG(WARNING)
          << "Failed to execute Subprocess::Hook in parent for child '"
          << pid << "': " << callback.error();

        os::close(pipes[1]);

        // Close the child-ends of the file descriptors that are created
        // by this function.
        os::close(stdinfds.read);
        os::close(stdoutfds.write);
        os::close(stderrfds.write);

        // Ensure the child is killed.
        ::kill(pid, SIGKILL);

        return Error(
            "Failed to execute Subprocess::Hook in parent for child '" +
            stringify(pid) + "': " + callback.error());
      }
    }

    // Now that we've executed the parent hooks, we can signal the child to
    // continue by writing to the pipe.
    char dummy;
    ssize_t length;
    while ((length = ::write(pipes[1], &dummy, sizeof(dummy))) == -1 &&
           errno == EINTR);

    os::close(pipes[1]);

    if (length != sizeof(dummy)) {
      // Ensure the child is killed.
      ::kill(pid, SIGKILL);

      // Close the child-ends of the file descriptors that are created
      // by this function.
      os::close(stdinfds.read);
      os::close(stdoutfds.write);
      os::close(stderrfds.write);
      return Error("Failed to synchronize child process");
    }
  }

  return pid;
}

}  // namespace internal {
}  // namespace process {

#endif // __PROCESS_POSIX_SUBPROCESS_HPP__
<commit_msg>Fixed compilation error caused by prctl and recent subprocess change.<commit_after>// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License

#ifndef __PROCESS_POSIX_SUBPROCESS_HPP__
#define __PROCESS_POSIX_SUBPROCESS_HPP__

#ifdef __linux__
#include <sys/prctl.h>
#endif // __linux__
#include <sys/types.h>

#include <string>

#include <glog/logging.h>

#include <process/subprocess.hpp>

#include <stout/error.hpp>
#include <stout/foreach.hpp>
#include <stout/nothing.hpp>
#include <stout/lambda.hpp>
#include <stout/none.hpp>
#include <stout/option.hpp>
#include <stout/os.hpp>
#include <stout/try.hpp>
#include <stout/unreachable.hpp>

#include <stout/os/close.hpp>
#include <stout/os/environment.hpp>
#include <stout/os/fcntl.hpp>
#include <stout/os/signals.hpp>
#include <stout/os/strerror.hpp>

using std::map;
using std::string;
using std::vector;

namespace process {

using InputFileDescriptors = Subprocess::IO::InputFileDescriptors;
using OutputFileDescriptors = Subprocess::IO::OutputFileDescriptors;

namespace internal {

// This function will invoke `os::close` on all specified file
// descriptors that are valid (i.e., not `None` and >= 0).
inline void close(
    const InputFileDescriptors& stdinfds,
    const OutputFileDescriptors& stdoutfds,
    const OutputFileDescriptors& stderrfds)
{
  int fds[6] = {
    stdinfds.read, stdinfds.write.getOrElse(-1),
    stdoutfds.read.getOrElse(-1), stdoutfds.write,
    stderrfds.read.getOrElse(-1), stderrfds.write
  };

  foreach (int fd, fds) {
    if (fd >= 0) {
      os::close(fd);
    }
  }
}

// This function will invoke `os::cloexec` on all specified file
// descriptors that are valid (i.e., not `None` and >= 0).
inline Try<Nothing> cloexec(
    const InputFileDescriptors& stdinfds,
    const OutputFileDescriptors& stdoutfds,
    const OutputFileDescriptors& stderrfds)
{
  int fds[6] = {
    stdinfds.read, stdinfds.write.getOrElse(-1),
    stdoutfds.read.getOrElse(-1), stdoutfds.write,
    stderrfds.read.getOrElse(-1), stderrfds.write
  };

  foreach (int fd, fds) {
    if (fd >= 0) {
      Try<Nothing> cloexec = os::cloexec(fd);
      if (cloexec.isError()) {
        return Error(cloexec.error());
      }
    }
  }

  return Nothing();
}

inline pid_t defaultClone(const lambda::function<int()>& func)
{
  pid_t pid = ::fork();
  if (pid == -1) {
    return -1;
  } else if (pid == 0) {
    // Child.
    ::exit(func());
    UNREACHABLE();
  } else {
    // Parent.
    return pid;
  }
}

inline void signalHandler(int signal)
{
  // Send SIGKILL to every process in the process group of the
  // calling process.
  kill(0, SIGKILL);
  abort();
}

// Creates a seperate watchdog process to monitor the child process and
// kill it in case the parent process dies.
//
// NOTE: This function needs to be async signal safe. In fact,
// all the library functions we used in this function are async
// signal safe.
inline int watchdogProcess()
{
#ifdef __linux__
  // Send SIGTERM to the current process if the parent (i.e., the
  // slave) exits.
  // NOTE:: This function should always succeed because we are passing
  // in a valid signal.
  prctl(PR_SET_PDEATHSIG, SIGTERM);

  // Put the current process into a separate process group so that
  // we can kill it and all its children easily.
  if (setpgid(0, 0) != 0) {
    abort();
  }

  // Install a SIGTERM handler which will kill the current process
  // group. Since we already setup the death signal above, the
  // signal handler will be triggered when the parent (e.g., the
  // slave) exits.
  if (os::signals::install(SIGTERM, &signalHandler) != 0) {
    abort();
  }

  pid_t pid = fork();
  if (pid == -1) {
    abort();
  } else if (pid == 0) {
    // Child. This is the process that is going to exec the
    // process if zero is returned.

    // We setup death signal for the process as well in case
    // someone, though unlikely, accidentally kill the parent of
    // this process (the bookkeeping process).
    prctl(PR_SET_PDEATHSIG, SIGKILL);

    // NOTE: We don't need to clear the signal handler explicitly
    // because the subsequent 'exec' will clear them.
    return 0;
  } else {
    // Parent. This is the bookkeeping process which will wait for
    // the child process to finish.

    // Close the files to prevent interference on the communication
    // between the slave and the child process.
    ::close(STDIN_FILENO);
    ::close(STDOUT_FILENO);
    ::close(STDERR_FILENO);

    // Block until the child process finishes.
    int status = 0;
    if (waitpid(pid, &status, 0) == -1) {
      abort();
    }

    // Forward the exit status if the child process exits normally.
    if (WIFEXITED(status)) {
      _exit(WEXITSTATUS(status));
    }

    abort();
    UNREACHABLE();
  }
#endif
  return 0;
}

// The main entry of the child process.
//
// NOTE: This function has to be async signal safe.
inline int childMain(
    const string& path,
    char** argv,
    char** envp,
    const Setsid set_sid,
    const InputFileDescriptors& stdinfds,
    const OutputFileDescriptors& stdoutfds,
    const OutputFileDescriptors& stderrfds,
    bool blocking,
    int pipes[2],
    const Option<string>& working_directory,
    const Watchdog watchdog)
{
  // Close parent's end of the pipes.
  if (stdinfds.write.isSome()) {
    ::close(stdinfds.write.get());
  }
  if (stdoutfds.read.isSome()) {
    ::close(stdoutfds.read.get());
  }
  if (stderrfds.read.isSome()) {
    ::close(stderrfds.read.get());
  }

  // Currently we will block the child's execution of the new process
  // until all the parent hooks (if any) have executed.
  if (blocking) {
    ::close(pipes[1]);
  }

  // Redirect I/O for stdin/stdout/stderr.
  while (::dup2(stdinfds.read, STDIN_FILENO) == -1 && errno == EINTR);
  while (::dup2(stdoutfds.write, STDOUT_FILENO) == -1 && errno == EINTR);
  while (::dup2(stderrfds.write, STDERR_FILENO) == -1 && errno == EINTR);

  // Close the copies. We need to make sure that we do not close the
  // file descriptor assigned to stdin/stdout/stderr in case the
  // parent has closed stdin/stdout/stderr when calling this
  // function (in that case, a dup'ed file descriptor may have the
  // same file descriptor number as stdin/stdout/stderr).
  if (stdinfds.read != STDIN_FILENO &&
      stdinfds.read != STDOUT_FILENO &&
      stdinfds.read != STDERR_FILENO) {
    ::close(stdinfds.read);
  }
  if (stdoutfds.write != STDIN_FILENO &&
      stdoutfds.write != STDOUT_FILENO &&
      stdoutfds.write != STDERR_FILENO) {
    ::close(stdoutfds.write);
  }
  if (stderrfds.write != STDIN_FILENO &&
      stderrfds.write != STDOUT_FILENO &&
      stderrfds.write != STDERR_FILENO) {
    ::close(stderrfds.write);
  }

  if (blocking) {
    // Do a blocking read on the pipe until the parent signals us to
    // continue.
    char dummy;
    ssize_t length;
    while ((length = ::read(pipes[0], &dummy, sizeof(dummy))) == -1 &&
          errno == EINTR);

    if (length != sizeof(dummy)) {
      ABORT("Failed to synchronize with parent");
    }

    // Now close the pipe as we don't need it anymore.
    ::close(pipes[0]);
  }

  // Move to a different session (and new process group) so we're
  // independent from the caller's session (otherwise children will
  // receive SIGHUP if the slave exits).
  if (set_sid == SETSID) {
    // POSIX guarantees a forked child's pid does not match any existing
    // process group id so only a single `setsid()` is required and the
    // session id will be the pid.
    if (::setsid() == -1) {
      ABORT("Failed to put child in a new session");
    }
  }

  if (working_directory.isSome()) {
    if (::chdir(working_directory->c_str()) == -1) {
      ABORT("Failed to change directory");
    }
  }

  // If the child process should die together with its parent we spawn a
  // separate watchdog process which kills the child when the parent dies.
  //
  // NOTE: The watchdog process sets the process group id in order for it and
  // its child processes to be killed together. We should not (re)set the sid
  // after this.
  if (watchdog == MONITOR) {
    watchdogProcess();
  }

  os::execvpe(path.c_str(), argv, envp);

  ABORT("Failed to os::execvpe on path '" + path + "': " + os::strerror(errno));
}

inline Try<pid_t> cloneChild(
    const string& path,
    vector<string> argv,
    const Setsid set_sid,
    const Option<map<string, string>>& environment,
    const Option<lambda::function<
        pid_t(const lambda::function<int()>&)>>& _clone,
    const vector<Subprocess::Hook>& parent_hooks,
    const Option<string>& working_directory,
    const Watchdog watchdog,
    const InputFileDescriptors stdinfds,
    const OutputFileDescriptors stdoutfds,
    const OutputFileDescriptors stderrfds)
{
  // The real arguments that will be passed to 'os::execvpe'. We need
  // to construct them here before doing the clone as it might not be
  // async signal safe to perform the memory allocation.
  char** _argv = new char*[argv.size() + 1];
  for (size_t i = 0; i < argv.size(); i++) {
    _argv[i] = (char*) argv[i].c_str();
  }
  _argv[argv.size()] = NULL;

  // Like above, we need to construct the environment that we'll pass
  // to 'os::execvpe' as it might not be async-safe to perform the
  // memory allocations.
  char** envp = os::raw::environment();

  if (environment.isSome()) {
    // NOTE: We add 1 to the size for a NULL terminator.
    envp = new char*[environment.get().size() + 1];

    size_t index = 0;
    foreachpair (const string& key, const string& value, environment.get()) {
      string entry = key + "=" + value;
      envp[index] = new char[entry.size() + 1];
      strncpy(envp[index], entry.c_str(), entry.size() + 1);
      ++index;
    }

    envp[index] = NULL;
  }

  // Determine the function to clone the child process. If the user
  // does not specify the clone function, we will use the default.
  lambda::function<pid_t(const lambda::function<int()>&)> clone =
    (_clone.isSome() ? _clone.get() : defaultClone);

  // Currently we will block the child's execution of the new process
  // until all the `parent_hooks` (if any) have executed.
  int pipes[2];
  const bool blocking = !parent_hooks.empty();

  if (blocking) {
    // We assume this should not fail under reasonable conditions so we
    // use CHECK.
    CHECK_EQ(0, ::pipe(pipes));
  }

  // Now, clone the child process.
  pid_t pid = clone(lambda::bind(
      &childMain,
      path,
      _argv,
      envp,
      set_sid,
      stdinfds,
      stdoutfds,
      stderrfds,
      blocking,
      pipes,
      working_directory,
      watchdog));

  delete[] _argv;

  // Need to delete 'envp' if we had environment variables passed to
  // us and we needed to allocate the space.
  if (environment.isSome()) {
    CHECK_NE(os::raw::environment(), envp);

    // We ignore the last 'envp' entry since it is NULL.
    for (size_t index = 0; index < environment->size(); index++) {
      delete[] envp[index];
    }

    delete[] envp;
  }

  if (pid == -1) {
    // Save the errno as 'close' below might overwrite it.
    ErrnoError error("Failed to clone");
    internal::close(stdinfds, stdoutfds, stderrfds);

    if (blocking) {
      os::close(pipes[0]);
      os::close(pipes[1]);
    }

    return error;
  }

  if (blocking) {
    os::close(pipes[0]);

    // Run the parent hooks.
    foreach (const Subprocess::Hook& hook, parent_hooks) {
      Try<Nothing> callback = hook.parent_callback(pid);

      // If the hook callback fails, we shouldn't proceed with the
      // execution and hence the child process should be killed.
      if (callback.isError()) {
        LOG(WARNING)
          << "Failed to execute Subprocess::Hook in parent for child '"
          << pid << "': " << callback.error();

        os::close(pipes[1]);

        // Close the child-ends of the file descriptors that are created
        // by this function.
        os::close(stdinfds.read);
        os::close(stdoutfds.write);
        os::close(stderrfds.write);

        // Ensure the child is killed.
        ::kill(pid, SIGKILL);

        return Error(
            "Failed to execute Subprocess::Hook in parent for child '" +
            stringify(pid) + "': " + callback.error());
      }
    }

    // Now that we've executed the parent hooks, we can signal the child to
    // continue by writing to the pipe.
    char dummy;
    ssize_t length;
    while ((length = ::write(pipes[1], &dummy, sizeof(dummy))) == -1 &&
           errno == EINTR);

    os::close(pipes[1]);

    if (length != sizeof(dummy)) {
      // Ensure the child is killed.
      ::kill(pid, SIGKILL);

      // Close the child-ends of the file descriptors that are created
      // by this function.
      os::close(stdinfds.read);
      os::close(stdoutfds.write);
      os::close(stderrfds.write);
      return Error("Failed to synchronize child process");
    }
  }

  return pid;
}

}  // namespace internal {
}  // namespace process {

#endif // __PROCESS_POSIX_SUBPROCESS_HPP__
<|endoftext|>