<commit_before>#include <sirius.h>
#include <thread>

using namespace sirius;

void test1(vector3d<int> const& dims__, double cutoff__, device_t pu__)
{
    printf("test1\n");
    matrix3d<double> M;
    M(0, 0) = M(1, 1) = M(2, 2) = 1.0;

    FFT3D fft(find_translations(cutoff__, M), mpi_comm_world(), pu__);

    Gvec gvec(M, cutoff__, mpi_comm_world(), false);
    Gvec_partition gvecp(gvec, mpi_comm_world(), mpi_comm_self());

    Gvec gvec_r(M, cutoff__, mpi_comm_world(), true);
    Gvec_partition gvecp_r(gvec_r, mpi_comm_world(), mpi_comm_self());

    if (gvec_r.num_gvec() != gvec.num_gvec() / 2 + 1) {
        printf("wrong number of reduced G-vectors");
        exit(1);
    }

    fft.prepare(gvecp_r);

    printf("num_gvec: %i, num_gvec_reduced: %i\n", gvec.num_gvec(), gvec_r.num_gvec());
    printf("num_gvec_loc: %i %i\n", gvec.gvec_count(mpi_comm_world().rank()), gvec_r.gvec_count(mpi_comm_world().rank()));
    printf("num_z_col: %i, num_z_col_reduced: %i\n", gvec.num_zcol(), gvec_r.num_zcol());

    mdarray<double_complex, 1> phi(gvecp_r.gvec_count_fft());
    for (int i = 0; i < gvecp_r.gvec_count_fft(); i++) {
        phi(i) = type_wrapper<double_complex>::random();
    }
    phi(0) = 1.0;
    fft.transform<1>(&phi[0]);
    #ifdef __GPU
    if (pu__ == GPU) {
        fft.buffer().copy_to_host();
    }
    #endif

    for (int i = 0; i < fft.local_size(); i++) {
        if (fft.buffer(i).imag() > 1e-10) {
            printf("function is not real at idx = %i, image value: %18.12f\n", i, fft.buffer(i).imag());
            exit(1);
        }
    }
    mdarray<double_complex, 1> phi1(gvecp_r.gvec_count_fft());
    fft.transform<-1>(&phi1[0]);

    double rms = 0;
    for (int i = 0; i < gvecp_r.gvec_count_fft(); i++) {
        rms += std::pow(std::abs(phi(i) - phi1(i)), 2);
    }
    rms = std::sqrt(rms / gvecp_r.gvec_count_fft());
    printf("rms: %18.12f\n", rms);
    if (rms > 1e-13) {
        printf("functions are different\n");
        exit(1);
    }

    fft.dismiss();
}

void test2(vector3d<int> const& dims__, double cutoff__, device_t pu__)
{
    printf("test2\n");
    matrix3d<double> M;
    M(0, 0) = M(1, 1) = M(2, 2) = 1.0;

    FFT3D fft(find_translations(cutoff__, M), mpi_comm_world(), pu__);

    Gvec gvec_r(M, cutoff__, mpi_comm_world(), true);
    Gvec_partition gvecp_r(gvec_r, mpi_comm_world(), mpi_comm_self());

    fft.prepare(gvecp_r);

    mdarray<double_complex, 1> phi1(gvecp_r.gvec_count_fft());
    mdarray<double_complex, 1> phi2(gvecp_r.gvec_count_fft());
    mdarray<double_complex, 1> phi1_rg(fft.local_size());
    mdarray<double_complex, 1> phi2_rg(fft.local_size());

    for (int i = 0; i < gvecp_r.gvec_count_fft(); i++) {
        phi1(i) = type_wrapper<double_complex>::random();
        phi2(i) = type_wrapper<double_complex>::random();
    }
    phi1(0) = 1.0;
    phi2(0) = 1.0;

    fft.transform<1>(&phi1(0));
    fft.output(&phi1_rg(0));

    fft.transform<1>(&phi2(0));
    fft.output(&phi2_rg(0));

    for (int i = 0; i < fft.local_size(); i++) {
        if (phi1_rg(i).imag() > 1e-10) {
            printf("phi1 is not real at idx = %i, image value: %18.12f\n", i, phi1_rg(i).imag());
            exit(1);
        }
        if (phi2_rg(i).imag() > 1e-10) {
            printf("phi2 is not real at idx = %i, image value: %18.12f\n", i, phi2_rg(i).imag());
            exit(1);
        }
    }

    fft.transform<1>(&phi1(0), &phi2(0));

    mdarray<double_complex, 1> phi12_rg(fft.local_size());
    fft.output(&phi12_rg(0));

    //printf("phi1(0)=%18.10f\n", phi1_rg(0));
    //printf("phi2(0)=%18.10f\n", phi2_rg(0));

    for (int i = 0; i < fft.local_size(); i++) {
        if (std::abs(double_complex(phi1_rg(i).real(), phi2_rg(i).real()) - phi12_rg(i)) > 1e-10) {
            printf("functions don't match\n");
            printf("phi1: %18.10f\n", phi1_rg(i).real());
            printf("phi2: %18.10f\n", phi2_rg(i).real());
            printf("complex phi: %18.10f %18.10f\n", fft.buffer(i).real(), fft.buffer(i).imag());
            exit(1);
        }
    }

    mdarray<double_complex, 1> phi1_bt(gvecp_r.gvec_count_fft());
    mdarray<double_complex, 1> phi2_bt(gvecp_r.gvec_count_fft());
    fft.transform<-1>(&phi1_bt(0), &phi2_bt(0));

    double diff = 0;
    for (int i = 0; i < gvecp_r.gvec_count_fft(); i++) {
        diff += std::abs(phi1(i) - phi1_bt(i));
        diff += std::abs(phi2(i) - phi2_bt(i));
    }
    diff /= gvecp_r.gvec_count_fft();
    printf("diff: %18.10f\n", diff);
    if (diff > 1e-13) {
        printf("functions are different\n");
        exit(1);
    }

    fft.dismiss();
}

#ifdef __GPU
void test3(vector3d<int> const& dims__, double cutoff__)
{
    printf("test3\n");
    matrix3d<double> M;
    M(0, 0) = M(1, 1) = M(2, 2) = 1.0;

    FFT3D fft(find_translations(cutoff__, M), mpi_comm_world(), GPU);

    Gvec gvec_r(M, cutoff__, mpi_comm_world(), mpi_comm_world(), mpi_comm_self(), true);

    fft.prepare(gvec_r.partition());

    mdarray<double_complex, 1> phi1(gvec_r.partition().gvec_count_fft(), memory_t::host | memory_t::device);
    mdarray<double_complex, 1> phi2(gvec_r.partition().gvec_count_fft(), memory_t::host | memory_t::device);
    mdarray<double_complex, 1> phi1_rg(fft.local_size());
    mdarray<double_complex, 1> phi2_rg(fft.local_size());

    for (int i = 0; i < gvec_r.partition().gvec_count_fft(); i++) {
        phi1(i) = type_wrapper<double_complex>::random();
        phi2(i) = type_wrapper<double_complex>::random();
    }
    phi1(0) = 1.0;
    phi2(0) = 1.0;

    phi1.copy<memory_t::host, memory_t::device>();
    phi2.copy<memory_t::host, memory_t::device>();

    fft.transform<1, GPU>(phi1.at<GPU>());
    fft.output(&phi1_rg(0));

    fft.transform<1, GPU>(phi2.at<GPU>());
    fft.output(&phi2_rg(0));

    for (int i = 0; i < fft.local_size(); i++) {
        if (phi1_rg(i).imag() > 1e-10) {
            printf("phi1 is not real at idx = %i, image value: %18.12f\n", i, phi1_rg(i).imag());
            exit(1);
        }
        if (phi2_rg(i).imag() > 1e-10) {
            printf("phi2 is not real at idx = %i, image value: %18.12f\n", i, phi2_rg(i).imag());
            exit(1);
        }
    }

    fft.transform<1, GPU>(phi1.at<GPU>(), phi2.at<GPU>());

    mdarray<double_complex, 1> phi12_rg(fft.local_size());
    fft.output(&phi12_rg(0));

    //printf("phi1(0)=%18.10f\n", phi1_rg(0));
    //printf("phi2(0)=%18.10f\n", phi2_rg(0));

    for (int i = 0; i < fft.local_size(); i++) {
        if (std::abs(double_complex(phi1_rg(i).real(), phi2_rg(i).real()) - phi12_rg(i)) > 1e-10) {
            printf("functions don't match\n");
            printf("phi1: %18.10f\n", phi1_rg(i).real());
            printf("phi2: %18.10f\n", phi2_rg(i).real());
            printf("complex phi: %18.10f %18.10f\n", fft.buffer(i).real(), fft.buffer(i).imag());
            exit(1);
        }
    }

    mdarray<double_complex, 1> phi1_bt(gvec_r.partition().gvec_count_fft(), memory_t::host | memory_t::device);
    mdarray<double_complex, 1> phi2_bt(gvec_r.partition().gvec_count_fft(), memory_t::host | memory_t::device);
    fft.transform<-1, GPU>(phi1_bt.at<GPU>(), phi2_bt.at<GPU>());

    phi1_bt.copy<memory_t::device, memory_t::host>();
    phi2_bt.copy<memory_t::device, memory_t::host>();

    double diff{0};
    for (int i = 0; i < gvec_r.partition().gvec_count_fft(); i++) {
        diff += std::abs(phi1(i) - phi1_bt(i));
        diff += std::abs(phi2(i) - phi2_bt(i));
    }
    diff /= gvec_r.partition().gvec_count_fft();
    printf("diff: %18.10f\n", diff);
    if (diff > 1e-13) {
        printf("functions are different\n");
        exit(1);
    }

    fft.dismiss();
}
#endif

int main(int argn, char **argv)
{
    cmd_args args;
    args.register_key("--dims=", "{vector3d<int>} FFT dimensions");
    args.register_key("--cutoff=", "{double} cutoff radius in G-space");

    args.parse_args(argn, argv);
    if (args.exist("help"))
    {
        printf("Usage: %s [options]\n", argv[0]);
        args.print_help();
        return 0;
    }

    std::vector<int> vd = args.value< std::vector<int> >("dims", {132, 132, 132});
    vector3d<int> dims(vd[0], vd[1], vd[2]);
    double cutoff = args.value<double>("cutoff", 50);

    sirius::initialize(1);

    test1(dims, cutoff, CPU);
    test2(dims, cutoff, CPU);
    #ifdef __GPU
    test1(dims, cutoff, GPU);
    test2(dims, cutoff, GPU);
    test3(dims, cutoff);
    #endif

    sirius::finalize();

    return 0;
}
<commit_msg>fix test<commit_after>#include <sirius.h>
#include <thread>

using namespace sirius;

void test1(vector3d<int> const& dims__, double cutoff__, device_t pu__)
{
    printf("test1\n");
    matrix3d<double> M;
    M(0, 0) = M(1, 1) = M(2, 2) = 1.0;

    FFT3D fft(find_translations(cutoff__, M), mpi_comm_world(), pu__);

    Gvec gvec(M, cutoff__, mpi_comm_world(), false);
    Gvec_partition gvecp(gvec, mpi_comm_world(), mpi_comm_self());

    Gvec gvec_r(M, cutoff__, mpi_comm_world(), true);
    Gvec_partition gvecp_r(gvec_r, mpi_comm_world(), mpi_comm_self());

    if (gvec_r.num_gvec() != gvec.num_gvec() / 2 + 1) {
        printf("wrong number of reduced G-vectors");
        exit(1);
    }

    fft.prepare(gvecp_r);

    printf("num_gvec: %i, num_gvec_reduced: %i\n", gvec.num_gvec(), gvec_r.num_gvec());
    printf("num_gvec_loc: %i %i\n", gvec.gvec_count(mpi_comm_world().rank()), gvec_r.gvec_count(mpi_comm_world().rank()));
    printf("num_z_col: %i, num_z_col_reduced: %i\n", gvec.num_zcol(), gvec_r.num_zcol());

    mdarray<double_complex, 1> phi(gvecp_r.gvec_count_fft());
    for (int i = 0; i < gvecp_r.gvec_count_fft(); i++) {
        phi(i) = type_wrapper<double_complex>::random();
    }
    phi(0) = 1.0;
    fft.transform<1>(&phi[0]);
    #ifdef __GPU
    if (pu__ == GPU) {
        fft.buffer().copy_to_host();
    }
    #endif

    for (int i = 0; i < fft.local_size(); i++) {
        if (fft.buffer(i).imag() > 1e-10) {
            printf("function is not real at idx = %i, image value: %18.12f\n", i, fft.buffer(i).imag());
            exit(1);
        }
    }
    mdarray<double_complex, 1> phi1(gvecp_r.gvec_count_fft());
    fft.transform<-1>(&phi1[0]);

    double rms = 0;
    for (int i = 0; i < gvecp_r.gvec_count_fft(); i++) {
        rms += std::pow(std::abs(phi(i) - phi1(i)), 2);
    }
    rms = std::sqrt(rms / gvecp_r.gvec_count_fft());
    printf("rms: %18.12f\n", rms);
    if (rms > 1e-13) {
        printf("functions are different\n");
        exit(1);
    }

    fft.dismiss();
}

void test2(vector3d<int> const& dims__, double cutoff__, device_t pu__)
{
    printf("test2\n");
    matrix3d<double> M;
    M(0, 0) = M(1, 1) = M(2, 2) = 1.0;

    FFT3D fft(find_translations(cutoff__, M), mpi_comm_world(), pu__);

    Gvec gvec_r(M, cutoff__, mpi_comm_world(), true);
    Gvec_partition gvecp_r(gvec_r, mpi_comm_world(), mpi_comm_self());

    fft.prepare(gvecp_r);

    mdarray<double_complex, 1> phi1(gvecp_r.gvec_count_fft());
    mdarray<double_complex, 1> phi2(gvecp_r.gvec_count_fft());
    mdarray<double_complex, 1> phi1_rg(fft.local_size());
    mdarray<double_complex, 1> phi2_rg(fft.local_size());

    for (int i = 0; i < gvecp_r.gvec_count_fft(); i++) {
        phi1(i) = type_wrapper<double_complex>::random();
        phi2(i) = type_wrapper<double_complex>::random();
    }
    phi1(0) = 1.0;
    phi2(0) = 1.0;

    fft.transform<1>(&phi1(0));
    fft.output(&phi1_rg(0));

    fft.transform<1>(&phi2(0));
    fft.output(&phi2_rg(0));

    for (int i = 0; i < fft.local_size(); i++) {
        if (phi1_rg(i).imag() > 1e-10) {
            printf("phi1 is not real at idx = %i, image value: %18.12f\n", i, phi1_rg(i).imag());
            exit(1);
        }
        if (phi2_rg(i).imag() > 1e-10) {
            printf("phi2 is not real at idx = %i, image value: %18.12f\n", i, phi2_rg(i).imag());
            exit(1);
        }
    }

    fft.transform<1>(&phi1(0), &phi2(0));

    mdarray<double_complex, 1> phi12_rg(fft.local_size());
    fft.output(&phi12_rg(0));

    //printf("phi1(0)=%18.10f\n", phi1_rg(0));
    //printf("phi2(0)=%18.10f\n", phi2_rg(0));

    for (int i = 0; i < fft.local_size(); i++) {
        if (std::abs(double_complex(phi1_rg(i).real(), phi2_rg(i).real()) - phi12_rg(i)) > 1e-10) {
            printf("functions don't match\n");
            printf("phi1: %18.10f\n", phi1_rg(i).real());
            printf("phi2: %18.10f\n", phi2_rg(i).real());
            printf("complex phi: %18.10f %18.10f\n", fft.buffer(i).real(), fft.buffer(i).imag());
            exit(1);
        }
    }

    mdarray<double_complex, 1> phi1_bt(gvecp_r.gvec_count_fft());
    mdarray<double_complex, 1> phi2_bt(gvecp_r.gvec_count_fft());
    fft.transform<-1>(&phi1_bt(0), &phi2_bt(0));

    double diff = 0;
    for (int i = 0; i < gvecp_r.gvec_count_fft(); i++) {
        diff += std::abs(phi1(i) - phi1_bt(i));
        diff += std::abs(phi2(i) - phi2_bt(i));
    }
    diff /= gvecp_r.gvec_count_fft();
    printf("diff: %18.10f\n", diff);
    if (diff > 1e-13) {
        printf("functions are different\n");
        exit(1);
    }

    fft.dismiss();
}

#ifdef __GPU
void test3(vector3d<int> const& dims__, double cutoff__)
{
    printf("test3\n");
    matrix3d<double> M;
    M(0, 0) = M(1, 1) = M(2, 2) = 1.0;

    FFT3D fft(find_translations(cutoff__, M), mpi_comm_world(), GPU);

    Gvec gvec_r(M, cutoff__, mpi_comm_world(), true);
    Gvec_partition gvecp_r(gvec_r, mpi_comm_world(), mpi_comm_self());

    fft.prepare(gvecp_r);

    mdarray<double_complex, 1> phi1(gvecp_r.gvec_count_fft(), memory_t::host | memory_t::device);
    mdarray<double_complex, 1> phi2(gvecp_r.gvec_count_fft(), memory_t::host | memory_t::device);
    mdarray<double_complex, 1> phi1_rg(fft.local_size());
    mdarray<double_complex, 1> phi2_rg(fft.local_size());

    for (int i = 0; i < gvecp_r.gvec_count_fft(); i++) {
        phi1(i) = type_wrapper<double_complex>::random();
        phi2(i) = type_wrapper<double_complex>::random();
    }
    phi1(0) = 1.0;
    phi2(0) = 1.0;

    phi1.copy<memory_t::host, memory_t::device>();
    phi2.copy<memory_t::host, memory_t::device>();

    fft.transform<1, GPU>(phi1.at<GPU>());
    fft.output(&phi1_rg(0));

    fft.transform<1, GPU>(phi2.at<GPU>());
    fft.output(&phi2_rg(0));

    for (int i = 0; i < fft.local_size(); i++) {
        if (phi1_rg(i).imag() > 1e-10) {
            printf("phi1 is not real at idx = %i, image value: %18.12f\n", i, phi1_rg(i).imag());
            exit(1);
        }
        if (phi2_rg(i).imag() > 1e-10) {
            printf("phi2 is not real at idx = %i, image value: %18.12f\n", i, phi2_rg(i).imag());
            exit(1);
        }
    }

    fft.transform<1, GPU>(phi1.at<GPU>(), phi2.at<GPU>());

    mdarray<double_complex, 1> phi12_rg(fft.local_size());
    fft.output(&phi12_rg(0));

    //printf("phi1(0)=%18.10f\n", phi1_rg(0));
    //printf("phi2(0)=%18.10f\n", phi2_rg(0));

    for (int i = 0; i < fft.local_size(); i++) {
        if (std::abs(double_complex(phi1_rg(i).real(), phi2_rg(i).real()) - phi12_rg(i)) > 1e-10) {
            printf("functions don't match\n");
            printf("phi1: %18.10f\n", phi1_rg(i).real());
            printf("phi2: %18.10f\n", phi2_rg(i).real());
            printf("complex phi: %18.10f %18.10f\n", fft.buffer(i).real(), fft.buffer(i).imag());
            exit(1);
        }
    }

    mdarray<double_complex, 1> phi1_bt(gvecp_r.gvec_count_fft(), memory_t::host | memory_t::device);
    mdarray<double_complex, 1> phi2_bt(gvecp_r.gvec_count_fft(), memory_t::host | memory_t::device);
    fft.transform<-1, GPU>(phi1_bt.at<GPU>(), phi2_bt.at<GPU>());

    phi1_bt.copy<memory_t::device, memory_t::host>();
    phi2_bt.copy<memory_t::device, memory_t::host>();

    double diff{0};
    for (int i = 0; i < gvecp_r.gvec_count_fft(); i++) {
        diff += std::abs(phi1(i) - phi1_bt(i));
        diff += std::abs(phi2(i) - phi2_bt(i));
    }
    diff /= gvecp_r.gvec_count_fft();
    printf("diff: %18.10f\n", diff);
    if (diff > 1e-13) {
        printf("functions are different\n");
        exit(1);
    }

    fft.dismiss();
}
#endif

int main(int argn, char **argv)
{
    cmd_args args;
    args.register_key("--dims=", "{vector3d<int>} FFT dimensions");
    args.register_key("--cutoff=", "{double} cutoff radius in G-space");

    args.parse_args(argn, argv);
    if (args.exist("help"))
    {
        printf("Usage: %s [options]\n", argv[0]);
        args.print_help();
        return 0;
    }

    std::vector<int> vd = args.value< std::vector<int> >("dims", {132, 132, 132});
    vector3d<int> dims(vd[0], vd[1], vd[2]);
    double cutoff = args.value<double>("cutoff", 50);

    sirius::initialize(1);

    test1(dims, cutoff, CPU);
    test2(dims, cutoff, CPU);
    #ifdef __GPU
    test1(dims, cutoff, GPU);
    test2(dims, cutoff, GPU);
    test3(dims, cutoff);
    #endif

    sirius::finalize();

    return 0;
}
<|endoftext|>


<commit_before>/*
 * Copyright 2013 Gustaf Räntilä
 *
 * 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 LIBQ_TYPE_TRAITS_CORE_HPP
#define LIBQ_TYPE_TRAITS_CORE_HPP

#include <type_traits>
#include <tuple>
#include <memory>
#include <vector>

namespace q {

#define Q_FORWARD( value ) \
	std::forward< \
		typename std::remove_reference< \
			typename std::decay< \
				decltype( value ) \
			>::type \
		>::type \
	>( value )

template< typename T > void ignore_result( T&& ) { }
template< typename T > void ignore_parameter( T&& ) { }

namespace detail {

template< typename T >
T identity_fn( T&& );

template< typename T >
typename std::remove_reference< T >::type identity_fn_noref( T&& );

} // namespace detail

/**
 * q::bool_type is std::true_type if value is true, otherwise q::bool_type is
 * std::false_type.
 */
template< bool value >
struct bool_type
: std::integral_constant< bool, value >
{ };

/**
 * Negates the meaning of is_* meta classes, such as std::is_void< >.
 *
 * The usage to invert e.g. std::is_void< T > would be:
 * q::negate< std::is_void >::template of< T >
 *
 * @see q::isnt
 */
template< template< typename... > class T >
struct negate
{
	template< typename... Args >
	struct of
	: bool_type< !T< Args... >::value >
	{
		typedef bool_type< !T< Args... >::value > type;
	};
};

/**
 * Simplified and specialized version of q::negate when the arguments are
 * immediately available.
 *
 * Usage:
 *   q::isnt< std::is_void, T >
 */
template< template< typename... > class T, typename... Args >
struct isnt
: negate< T >::template of< Args... >::type
{
	typedef typename negate< T >::template of< Args... >::type type;
};

/**
 * Similar to std::is_same, although this one disregards constness and
 * references.
 */
template<
	typename A,
	typename B >
struct is_same_type
: std::is_same<
	typename std::remove_reference<
		typename std::decay< A >::type
	>::type,
	typename std::remove_reference<
		typename std::decay< B >::type
	>::type
>
{ };

template< typename... T >
struct is_tuple
: std::false_type
{ };

template< typename... T >
struct is_tuple< std::tuple< T... > >
: std::true_type
{ };

template< typename T >
struct is_copyable
: bool_type<
	std::is_copy_constructible< T >::value ||
	std::is_copy_assignable< T >::value
>
{ };
/* TODO: Implement for variadic templates
template< typename... T >
struct is_copyable< std::tuple< T... > >
: is_copyable<  >
{ };
*/

template< typename T >
struct is_nothrow_copyable
: bool_type<
	std::is_nothrow_copy_constructible< T >::value ||
	std::is_nothrow_copy_assignable< T >::value
>
{ };

template< typename T >
struct is_movable
: bool_type<
	std::is_move_constructible< T >::value ||
	std::is_move_assignable< T >::value
>
{ };

template< typename T >
struct is_nothrow_movable
: bool_type<
	std::is_nothrow_move_constructible< T >::value ||
	std::is_nothrow_move_assignable< T >::value
>
{ };

template< typename T >
struct is_copyable_or_movable
: bool_type<
	is_copyable< T >::value ||
	is_movable< T >::value
>
{ };

template< typename T >
struct is_nothrow_copyable_or_movable
: bool_type<
	is_nothrow_copyable< T >::value ||
	is_nothrow_movable< T >::value
>
{ };

template< typename T >
struct is_shared_pointer
: std::false_type
{ };

template< typename T >
struct is_shared_pointer< std::shared_ptr< T > >
: std::true_type
{ };

template< typename T >
struct is_unique_pointer
: std::false_type
{ };

template< typename T >
struct is_unique_pointer< std::unique_ptr< T > >
: std::true_type
{ };

template< typename T >
struct is_pointer_like
: bool_type<
	std::is_pointer< T >::value ||
	is_shared_pointer< T >::value ||
	is_unique_pointer< T >::value
>
{ };

template< typename T >
struct remove_cv_ref
{
	typedef typename std::remove_cv<
		typename std::remove_reference< T >::type
	>::type type;
};

/**
 * This type is true only for char[N].
 */
template< typename T >
struct is_char_array
: public bool_type<
	std::is_array< typename remove_cv_ref< T >::type >::value &&
	std::is_same<
		typename remove_cv_ref<
			typename std::remove_extent<
				typename remove_cv_ref< T >::type
			>::type
		>::type,
		char
	>::value
>
{ };

/**
 * This type is true only for char*.
 */
template< typename T >
struct is_char_pointer
: public bool_type<
	std::is_pointer< typename remove_cv_ref< T >::type >::value &&
	std::is_same<
		typename remove_cv_ref<
			typename std::remove_pointer<
				typename remove_cv_ref< T >::type
			>::type
		>::type,
		char
	>::value
>
{ };

/**
 * This type is true for both char[N] and char*.
 */
template< typename T >
struct is_c_string
: public bool_type< is_char_array< T >::value or is_char_pointer< T >::value >
{ };

template< typename T >
struct is_vector
: std::false_type
{ };

template< typename T >
struct is_vector< std::vector< T > >
: std::true_type
{ };

template< typename Base, typename Derived >
struct is_base_of
: std::is_base_of< Base, Derived >
{ };

template< typename Base, typename Derived >
struct is_base_of< Base*, Derived* >
: std::is_base_of< Base, Derived >
{ };

template< typename... T >
struct not_implemented;

template< typename T >
typename std::decay< T >::type forward_decay( T&& v )
{
	return std::forward< T >( v );
}

template< typename... Args >
auto
forward_decay_as_tuple( Args&&... args )
-> decltype( std::make_tuple( forward_decay( std::forward< Args >( args ) )... ) )
{
	return std::make_tuple( forward_decay( std::forward< Args >( args ) )... );
}

template< typename... T >
struct decayed_tuple
{
	typedef std::tuple< typename std::decay< T >::type... > type;
};

template< typename T >
struct tuple_of
: std::tuple< T >
{
	typedef std::tuple< T > type;
};

template< >
struct tuple_of< void >
: std::tuple< >
{
	typedef std::tuple< > type;
};

template< typename... T >
struct tuple_of< std::tuple< T... > >
: std::tuple< T... >
{
	typedef std::tuple< T... > type;
};

} // namespace q

#endif // LIBQ_TYPE_TRAITS_CORE_HPP
<commit_msg>Q_FORWARD() shouldn't decay anything<commit_after>/*
 * Copyright 2013 Gustaf Räntilä
 *
 * 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 LIBQ_TYPE_TRAITS_CORE_HPP
#define LIBQ_TYPE_TRAITS_CORE_HPP

#include <type_traits>
#include <tuple>
#include <memory>
#include <vector>

namespace q {
#define Q_FORWARD( value ) \
	::std::forward< \
		decltype( value ) \
	>( value )

template< typename T > void ignore_result( T&& ) { }
template< typename T > void ignore_parameter( T&& ) { }

namespace detail {

template< typename T >
T identity_fn( T&& );

template< typename T >
typename std::remove_reference< T >::type identity_fn_noref( T&& );

} // namespace detail

/**
 * q::bool_type is std::true_type if value is true, otherwise q::bool_type is
 * std::false_type.
 */
template< bool value >
struct bool_type
: std::integral_constant< bool, value >
{ };

/**
 * Negates the meaning of is_* meta classes, such as std::is_void< >.
 *
 * The usage to invert e.g. std::is_void< T > would be:
 * q::negate< std::is_void >::template of< T >
 *
 * @see q::isnt
 */
template< template< typename... > class T >
struct negate
{
	template< typename... Args >
	struct of
	: bool_type< !T< Args... >::value >
	{
		typedef bool_type< !T< Args... >::value > type;
	};
};

/**
 * Simplified and specialized version of q::negate when the arguments are
 * immediately available.
 *
 * Usage:
 *   q::isnt< std::is_void, T >
 */
template< template< typename... > class T, typename... Args >
struct isnt
: negate< T >::template of< Args... >::type
{
	typedef typename negate< T >::template of< Args... >::type type;
};

/**
 * Similar to std::is_same, although this one disregards constness and
 * references.
 */
template<
	typename A,
	typename B >
struct is_same_type
: std::is_same<
	typename std::remove_reference<
		typename std::decay< A >::type
	>::type,
	typename std::remove_reference<
		typename std::decay< B >::type
	>::type
>
{ };

template< typename... T >
struct is_tuple
: std::false_type
{ };

template< typename... T >
struct is_tuple< std::tuple< T... > >
: std::true_type
{ };

template< typename T >
struct is_copyable
: bool_type<
	std::is_copy_constructible< T >::value ||
	std::is_copy_assignable< T >::value
>
{ };
/* TODO: Implement for variadic templates
template< typename... T >
struct is_copyable< std::tuple< T... > >
: is_copyable<  >
{ };
*/

template< typename T >
struct is_nothrow_copyable
: bool_type<
	std::is_nothrow_copy_constructible< T >::value ||
	std::is_nothrow_copy_assignable< T >::value
>
{ };

template< typename T >
struct is_movable
: bool_type<
	std::is_move_constructible< T >::value ||
	std::is_move_assignable< T >::value
>
{ };

template< typename T >
struct is_nothrow_movable
: bool_type<
	std::is_nothrow_move_constructible< T >::value ||
	std::is_nothrow_move_assignable< T >::value
>
{ };

template< typename T >
struct is_copyable_or_movable
: bool_type<
	is_copyable< T >::value ||
	is_movable< T >::value
>
{ };

template< typename T >
struct is_nothrow_copyable_or_movable
: bool_type<
	is_nothrow_copyable< T >::value ||
	is_nothrow_movable< T >::value
>
{ };

template< typename T >
struct is_shared_pointer
: std::false_type
{ };

template< typename T >
struct is_shared_pointer< std::shared_ptr< T > >
: std::true_type
{ };

template< typename T >
struct is_unique_pointer
: std::false_type
{ };

template< typename T >
struct is_unique_pointer< std::unique_ptr< T > >
: std::true_type
{ };

template< typename T >
struct is_pointer_like
: bool_type<
	std::is_pointer< T >::value ||
	is_shared_pointer< T >::value ||
	is_unique_pointer< T >::value
>
{ };

template< typename T >
struct remove_cv_ref
{
	typedef typename std::remove_cv<
		typename std::remove_reference< T >::type
	>::type type;
};

/**
 * This type is true only for char[N].
 */
template< typename T >
struct is_char_array
: public bool_type<
	std::is_array< typename remove_cv_ref< T >::type >::value &&
	std::is_same<
		typename remove_cv_ref<
			typename std::remove_extent<
				typename remove_cv_ref< T >::type
			>::type
		>::type,
		char
	>::value
>
{ };

/**
 * This type is true only for char*.
 */
template< typename T >
struct is_char_pointer
: public bool_type<
	std::is_pointer< typename remove_cv_ref< T >::type >::value &&
	std::is_same<
		typename remove_cv_ref<
			typename std::remove_pointer<
				typename remove_cv_ref< T >::type
			>::type
		>::type,
		char
	>::value
>
{ };

/**
 * This type is true for both char[N] and char*.
 */
template< typename T >
struct is_c_string
: public bool_type< is_char_array< T >::value or is_char_pointer< T >::value >
{ };

template< typename T >
struct is_vector
: std::false_type
{ };

template< typename T >
struct is_vector< std::vector< T > >
: std::true_type
{ };

template< typename Base, typename Derived >
struct is_base_of
: std::is_base_of< Base, Derived >
{ };

template< typename Base, typename Derived >
struct is_base_of< Base*, Derived* >
: std::is_base_of< Base, Derived >
{ };

template< typename... T >
struct not_implemented;

template< typename T >
typename std::decay< T >::type forward_decay( T&& v )
{
	return std::forward< T >( v );
}

template< typename... Args >
auto
forward_decay_as_tuple( Args&&... args )
-> decltype( std::make_tuple( forward_decay( std::forward< Args >( args ) )... ) )
{
	return std::make_tuple( forward_decay( std::forward< Args >( args ) )... );
}

template< typename... T >
struct decayed_tuple
{
	typedef std::tuple< typename std::decay< T >::type... > type;
};

template< typename T >
struct tuple_of
: std::tuple< T >
{
	typedef std::tuple< T > type;
};

template< >
struct tuple_of< void >
: std::tuple< >
{
	typedef std::tuple< > type;
};

template< typename... T >
struct tuple_of< std::tuple< T... > >
: std::tuple< T... >
{
	typedef std::tuple< T... > type;
};

} // namespace q

#endif // LIBQ_TYPE_TRAITS_CORE_HPP
<|endoftext|>


<commit_before>
#include "samson/common/MessagesOperations.h"

#include "samson/network/NetworkConnection.h"
#include "samson/network/NetworkListener.h"
#include "samson/network/SocketConnection.h"
#include "samson/network/NetworkConnection.h"

#include "CommonNetwork.h" // Own interface

namespace samson {

    void CommonNetwork::notify( engine::Notification* notification )
    {
        if (notification == NULL)
            LM_D(("notification == NULL"));

        // Check pennding packets to be removed after 1 minute disconnected
        // -------------------------------------------------------------------------------------
        NetworkManager::check();

        // Check disconnected elements to be removed ( if possible no threads or pending packets )
        // ----------------------------------------------------------------------------------------
        NetworkConnection * connection = NetworkManager::extractNextDisconnectedConnection();
        while (connection)
        {

            // Some notification about this disconnection
            NodeIdentifier node_identifier = connection->getNodeIdentifier();

            if( node_identifier.node_type == WorkerNode )
                report_worker_disconnected( node_identifier.id );

            if( node_identifier.node_type == DelilahNode )
                report_delilah_disconnected( node_identifier.id );

            // Remove connection
            delete connection;

            // Scan the next connection
            connection = NetworkManager::extractNextDisconnectedConnection();
        }

        // Check workers ( review connections , create new connections if possible , .... )
        // -------------------------------------------------------------------------------------

        if( cluster_information.getId() != 0 )
        {
            // Get a duplicate of current nodes involved in this cluster to connect to all of them
            au::vector<ClusterNode> nodes = cluster_information.getNodesToConnect( node_identifier );

            for ( size_t i = 0 ; i < nodes.size() ; i++ )
            {

                std::string name = NodeIdentifier( WorkerNode , nodes[i]->id ).getCodeName();

                if ( !NetworkManager::isConnected(name) )
                {
                    // Connect with this worker....
                    std::string host = nodes[i]->host;
                    int port = nodes[i]->port;
                    size_t worker_id = nodes[i]->id;

                    addWorkerConnection(worker_id, host, port);
                }

            }

            // Remove nodes
            nodes.clearVector();

        }

        // -------------------------------------------------------------------------------------

    }

    std::vector<size_t> CommonNetwork::getWorkerIds()
    {
        return cluster_information.getWorkerIds();
    }

    std::vector<size_t> CommonNetwork::getConnectedWorkerIds()
    {
        std::vector<size_t> workers = getWorkerIds();
        std::vector<size_t> connected_workers;

        for ( size_t i = 0 ; i < workers.size() ; i++ )
        {
            std::string connection_name = NodeIdentifier( WorkerNode , workers[i]).getCodeName();
            if( NetworkManager::isConnected(connection_name ) )
                connected_workers.push_back( workers[i] );
        }

        return connected_workers;
    }

    std::string CommonNetwork::str()
    {
        std::ostringstream output;

        output << "-----------------------------------------------\n";
        output << "NetworkManager (" << node_identifier.str() <<  ") \n";
        output << "-----------------------------------------------\n";

        output << NetworkManager::str();

        output << "-----------------------------------------------\n";

        return output.str();
    }

    // Add a new connection agains an additional worker to be added to the cluster
    Status CommonNetwork::addWorkerConnection( size_t worker_id , std::string host , int port )
    {
        NodeIdentifier node_identifier = NodeIdentifier( WorkerNode , worker_id );
        std::string name = node_identifier.getCodeName();

        // Check if we already have this connection
        if( NetworkManager::isConnected(name) )
            return Error;

        // Init connection
        SocketConnection* socket_connection;
        Status s = SocketConnection::newSocketConnection(host , port , &socket_connection );

        // If there is an error, just return the error
        if( s != OK )
            return s;

        // Create network connection with this socket
        NetworkConnection* network_connection = new NetworkConnection( name , socket_connection , this );

        // Set identifier since we know this worker id
        network_connection->setNodeIdentifier( node_identifier );

        // Sent hello packages rigth now to make sure if identity us
        network_connection->push( helloMessage( network_connection ) );

        // Add this new connection
        s = NetworkManager::add( network_connection );
        if( s != OK )
        {
            LM_W(("Error adding new connection for worker %s" , name.c_str() ));
            delete network_connection;
            return s;
        }

        // Notify about this connection
        report_worker_connected( worker_id );
        return OK;
    }

    Packet* CommonNetwork::helloMessage( NetworkConnection * target )
    {
        Packet* hello_packet = new Packet( Message::Hello );

        network::Hello* pb_hello =  hello_packet->message->mutable_hello();

        // Copy cluster information
        cluster_information.fill( pb_hello->mutable_cluster_information() );

        // Copy identification information
        node_identifier.fill( pb_hello->mutable_node_identifier() );

        // Copy user and password
        pb_hello->set_user( user );
        pb_hello->set_password( password );

        // Copy connection type
        pb_hello->set_connection_type(connection_type);

        if( !target )
            pb_hello->set_answer_hello_required(false);
        else
        {
            // Ask for Hello back if we still do not know who is the other...
            if(  target->getNodeIdentifier().node_type == UnknownNode )
                pb_hello->set_answer_hello_required(true);
            else
                pb_hello->set_answer_hello_required(false);
        }

        return hello_packet;
    }

    Status CommonNetwork::send( Packet* packet )
    {
        // Add more info to buffer name for debuggin
        if( packet->buffer )
            packet->buffer->addToName( au::str(" [in packet to %s]" , packet->to.str().c_str() ));

        //LM_W(("Sending packet %s to %s" ,  packet->str().c_str() , packet->to.str().c_str() ));

        if ( packet->to == node_identifier )
        {
            // Local loop
            packet->from = node_identifier;
            network_interface_receiver->schedule_receive( packet );
            return OK;
        }

        // Push a packet to a connection or eventually keep in queue to see if it connects back soon ;)
        return NetworkManager::send( packet );
    }

    // Receive a packet from a connection
    void CommonNetwork::receive( NetworkConnection* connection, Packet* packet )
    {

        if( packet->msgCode == Message::Hello )
        {
            processHello(connection, packet);
            return;
        }

        if( connection->getNodeIdentifier().node_type == UnknownNode )
            LM_X(1, ("Packet %s received from a non-identified node %s %s"
                     , packet->str().c_str()
                     , connection->getNodeIdentifier().str().c_str()
                     , connection->getName().c_str()
                     ));

        // Common interface to receive packets
        packet->from = connection->getNodeIdentifier();

        // Flush previous packets for me ( if any )
        if( network_interface_receiver )
        {
            while( pending_packets_for_me.size() > 0 )
            {
                Packet * previous_packet = pending_packets_for_me.front();
                pending_packets_for_me.pop_front();
                network_interface_receiver->schedule_receive( previous_packet );
            }

            // Schedule the new packet
            network_interface_receiver->schedule_receive( packet );
        }
        else
            pending_packets_for_me.push_back( packet );

    }

    void CommonNetwork::getInfo( ::std::ostringstream& output , std::string command , std::string format)
    {
        if( command == "main" )
        {
            // Do something
        }
        if( command == "cluster" )
           cluster_information.getInfo( output , format);
        else
        {
            if (format == "xml")
                output << "<error>Unknown network element " << command << "</error>\n";
            else
                output << "  \"error\" : \"unknown network element: " << command << "\"\r\n";
        }
    }

    void CommonNetwork::report_worker_connected( size_t id )
    {
        Packet * packet = new Packet( Message::NetworkNotification );
        packet->message->mutable_network_notification()->set_connected_worker_id(id);
        network_interface_receiver->receive( packet );
    }

    void CommonNetwork::report_worker_disconnected( size_t id )
    {
        Packet * packet = new Packet( Message::NetworkNotification );
        packet->message->mutable_network_notification()->set_disconnected_worker_id(id);
        network_interface_receiver->receive( packet );
    }

    void CommonNetwork::report_delilah_connected( size_t id )
    {
        Packet * packet = new Packet( Message::NetworkNotification );
        packet->message->mutable_network_notification()->set_connected_delilah_id(id);
        network_interface_receiver->receive( packet );
    }

    void CommonNetwork::report_delilah_disconnected( size_t id )
    {
        Packet * packet = new Packet( Message::NetworkNotification );
        packet->message->mutable_network_notification()->set_disconnected_delilah_id(id);
        network_interface_receiver->receive( packet );
    }

    NodeIdentifier CommonNetwork::getMynodeIdentifier()
    {
        return node_identifier;
    }

    std::string CommonNetwork::getHostForWorker(size_t worker_id)
    {
        return cluster_information.hostForWorker( worker_id );
    }

    unsigned short CommonNetwork::getPortForWorker(size_t worker_id)
    {
        return cluster_information.portForWorker( worker_id );
    }

    au::tables::Table* CommonNetwork::getClusterConnectionsTable()
    {
        au::tables::Table* table = new au::tables::Table( au::StringVector( "Worker" , "Host" , "Status"  ) );

        if ( cluster_information.getId() == 0 )
            table->setDefaultTitle("Not connected to any cluster");
        else
        {

            au::vector<ClusterNode> nodes = cluster_information.getNodes();

            for ( size_t i = 0 ; i < nodes.size() ; i++ )
            {
                au::StringVector values;
                values.push_back( au::str("%lu" , nodes[i]->id ) );
                values.push_back( au::str("%s:%d" , nodes[i]->host.c_str() , nodes[i]->port ) );

                NodeIdentifier ni = nodes[i]->getNodeIdentifier();
                std::string connection_name = ni.getCodeName();

                if( ni == node_identifier )
                    values.push_back("me");
                else
                    values.push_back( NetworkManager::getStatusForConnection(connection_name) );

                table->addRow( values );

            }

            std::string title = au::str("Cluster %lu ( version %lu )"
                                        , cluster_information.getId()
                                        , cluster_information.getVersion()
                                        );
            table->setDefaultTitle( title );
            nodes.clearVector();
        }

        return table;

    }

    std::vector<size_t> CommonNetwork::getDelilahIds()
    {
        return NetworkManager::getDelilahIds();
    }
    network::Collection* CommonNetwork::getConnectionsCollection( Visualization* visualization )
    {
        return NetworkManager::getConnectionsCollection(visualization);
    }
    size_t CommonNetwork::get_rate_in()
    {
        return NetworkManager::get_rate_in();
    }
    size_t CommonNetwork::get_rate_out()
    {
        return NetworkManager::get_rate_out();
    }

}
<commit_msg>SAMSON-1226 Fix memory leak in common network. Thanks Ken for this great detection<commit_after>
#include "samson/common/MessagesOperations.h"

#include "samson/network/NetworkConnection.h"
#include "samson/network/NetworkListener.h"
#include "samson/network/SocketConnection.h"
#include "samson/network/NetworkConnection.h"

#include "CommonNetwork.h" // Own interface

namespace samson {

    void CommonNetwork::notify( engine::Notification* notification )
    {
        if (notification == NULL)
            LM_D(("notification == NULL"));

        // Check pennding packets to be removed after 1 minute disconnected
        // -------------------------------------------------------------------------------------
        NetworkManager::check();

        // Check disconnected elements to be removed ( if possible no threads or pending packets )
        // ----------------------------------------------------------------------------------------
        NetworkConnection * connection = NetworkManager::extractNextDisconnectedConnection();
        while (connection)
        {

            // Some notification about this disconnection
            NodeIdentifier node_identifier = connection->getNodeIdentifier();

            if( node_identifier.node_type == WorkerNode )
                report_worker_disconnected( node_identifier.id );

            if( node_identifier.node_type == DelilahNode )
                report_delilah_disconnected( node_identifier.id );

            // Remove connection
            delete connection;

            // Scan the next connection
            connection = NetworkManager::extractNextDisconnectedConnection();
        }

        // Check workers ( review connections , create new connections if possible , .... )
        // -------------------------------------------------------------------------------------

        if( cluster_information.getId() != 0 )
        {
            // Get a duplicate of current nodes involved in this cluster to connect to all of them
            au::vector<ClusterNode> nodes = cluster_information.getNodesToConnect( node_identifier );

            for ( size_t i = 0 ; i < nodes.size() ; i++ )
            {

                std::string name = NodeIdentifier( WorkerNode , nodes[i]->id ).getCodeName();

                if ( !NetworkManager::isConnected(name) )
                {
                    // Connect with this worker....
                    std::string host = nodes[i]->host;
                    int port = nodes[i]->port;
                    size_t worker_id = nodes[i]->id;

                    addWorkerConnection(worker_id, host, port);
                }

            }

            // Remove nodes
            nodes.clearVector();

        }

        // -------------------------------------------------------------------------------------

    }

    std::vector<size_t> CommonNetwork::getWorkerIds()
    {
        return cluster_information.getWorkerIds();
    }

    std::vector<size_t> CommonNetwork::getConnectedWorkerIds()
    {
        std::vector<size_t> workers = getWorkerIds();
        std::vector<size_t> connected_workers;

        for ( size_t i = 0 ; i < workers.size() ; i++ )
        {
            std::string connection_name = NodeIdentifier( WorkerNode , workers[i]).getCodeName();
            if( NetworkManager::isConnected(connection_name ) )
                connected_workers.push_back( workers[i] );
        }

        return connected_workers;
    }

    std::string CommonNetwork::str()
    {
        std::ostringstream output;

        output << "-----------------------------------------------\n";
        output << "NetworkManager (" << node_identifier.str() <<  ") \n";
        output << "-----------------------------------------------\n";

        output << NetworkManager::str();

        output << "-----------------------------------------------\n";

        return output.str();
    }

    // Add a new connection agains an additional worker to be added to the cluster
    Status CommonNetwork::addWorkerConnection( size_t worker_id , std::string host , int port )
    {
        NodeIdentifier node_identifier = NodeIdentifier( WorkerNode , worker_id );
        std::string name = node_identifier.getCodeName();

        // Check if we already have this connection
        if( NetworkManager::isConnected(name) )
            return Error;

        // Init connection
        SocketConnection* socket_connection;
        Status s = SocketConnection::newSocketConnection(host , port , &socket_connection );

        // If there is an error, just return the error
        if( s != OK )
            return s;

        // Create network connection with this socket
        NetworkConnection* network_connection = new NetworkConnection( name , socket_connection , this );

        // Set identifier since we know this worker id
        network_connection->setNodeIdentifier( node_identifier );

        // Sent hello packages rigth now to make sure if identity us
        network_connection->push( helloMessage( network_connection ) );

        // Add this new connection
        s = NetworkManager::add( network_connection );
        if( s != OK )
        {
            LM_W(("Error adding new connection for worker %s" , name.c_str() ));
            delete network_connection;
            return s;
        }

        // Notify about this connection
        report_worker_connected( worker_id );
        return OK;
    }

    Packet* CommonNetwork::helloMessage( NetworkConnection * target )
    {
        Packet* hello_packet = new Packet( Message::Hello );

        network::Hello* pb_hello =  hello_packet->message->mutable_hello();

        // Copy cluster information
        cluster_information.fill( pb_hello->mutable_cluster_information() );

        // Copy identification information
        node_identifier.fill( pb_hello->mutable_node_identifier() );

        // Copy user and password
        pb_hello->set_user( user );
        pb_hello->set_password( password );

        // Copy connection type
        pb_hello->set_connection_type(connection_type);

        if( !target )
            pb_hello->set_answer_hello_required(false);
        else
        {
            // Ask for Hello back if we still do not know who is the other...
            if(  target->getNodeIdentifier().node_type == UnknownNode )
                pb_hello->set_answer_hello_required(true);
            else
                pb_hello->set_answer_hello_required(false);
        }

        return hello_packet;
    }

    Status CommonNetwork::send( Packet* packet )
    {
        // Add more info to buffer name for debuggin
        if( packet->buffer )
            packet->buffer->addToName( au::str(" [in packet to %s]" , packet->to.str().c_str() ));

        //LM_W(("Sending packet %s to %s" ,  packet->str().c_str() , packet->to.str().c_str() ));

        if ( packet->to == node_identifier )
        {
            // Local loop
            packet->from = node_identifier;
            network_interface_receiver->schedule_receive( packet );
            return OK;
        }

        // Push a packet to a connection or eventually keep in queue to see if it connects back soon ;)
        return NetworkManager::send( packet );
    }

    // Receive a packet from a connection
    void CommonNetwork::receive( NetworkConnection* connection, Packet* packet )
    {

        if( packet->msgCode == Message::Hello )
        {
            processHello(connection, packet);
            return;
        }

        if( connection->getNodeIdentifier().node_type == UnknownNode )
            LM_X(1, ("Packet %s received from a non-identified node %s %s"
                     , packet->str().c_str()
                     , connection->getNodeIdentifier().str().c_str()
                     , connection->getName().c_str()
                     ));

        // Common interface to receive packets
        packet->from = connection->getNodeIdentifier();

        // Flush previous packets for me ( if any )
        if( network_interface_receiver )
        {
            while( pending_packets_for_me.size() > 0 )
            {
                Packet * previous_packet = pending_packets_for_me.front();
                pending_packets_for_me.pop_front();
                network_interface_receiver->schedule_receive( previous_packet );
            }

            // Schedule the new packet
            network_interface_receiver->schedule_receive( packet );
        }
        else
            pending_packets_for_me.push_back( packet );

    }

    void CommonNetwork::getInfo( ::std::ostringstream& output , std::string command , std::string format)
    {
        if( command == "main" )
        {
            // Do something
        }
        if( command == "cluster" )
           cluster_information.getInfo( output , format);
        else
        {
            if (format == "xml")
                output << "<error>Unknown network element " << command << "</error>\n";
            else
                output << "  \"error\" : \"unknown network element: " << command << "\"\r\n";
        }
    }

    void CommonNetwork::report_worker_connected( size_t id )
    {
        Packet * packet = new Packet( Message::NetworkNotification );
        packet->message->mutable_network_notification()->set_connected_worker_id(id);
        network_interface_receiver->schedule_receive( packet );
    }

    void CommonNetwork::report_worker_disconnected( size_t id )
    {
        Packet * packet = new Packet( Message::NetworkNotification );
        packet->message->mutable_network_notification()->set_disconnected_worker_id(id);
        network_interface_receiver->schedule_receive( packet );
    }

    void CommonNetwork::report_delilah_connected( size_t id )
    {
        Packet * packet = new Packet( Message::NetworkNotification );
        packet->message->mutable_network_notification()->set_connected_delilah_id(id);
        network_interface_receiver->schedule_receive( packet );
    }

    void CommonNetwork::report_delilah_disconnected( size_t id )
    {
        Packet * packet = new Packet( Message::NetworkNotification );
        packet->message->mutable_network_notification()->set_disconnected_delilah_id(id);
        network_interface_receiver->schedule_receive( packet );
    }

    NodeIdentifier CommonNetwork::getMynodeIdentifier()
    {
        return node_identifier;
    }

    std::string CommonNetwork::getHostForWorker(size_t worker_id)
    {
        return cluster_information.hostForWorker( worker_id );
    }

    unsigned short CommonNetwork::getPortForWorker(size_t worker_id)
    {
        return cluster_information.portForWorker( worker_id );
    }

    au::tables::Table* CommonNetwork::getClusterConnectionsTable()
    {
        au::tables::Table* table = new au::tables::Table( au::StringVector( "Worker" , "Host" , "Status"  ) );

        if ( cluster_information.getId() == 0 )
            table->setDefaultTitle("Not connected to any cluster");
        else
        {

            au::vector<ClusterNode> nodes = cluster_information.getNodes();

            for ( size_t i = 0 ; i < nodes.size() ; i++ )
            {
                au::StringVector values;
                values.push_back( au::str("%lu" , nodes[i]->id ) );
                values.push_back( au::str("%s:%d" , nodes[i]->host.c_str() , nodes[i]->port ) );

                NodeIdentifier ni = nodes[i]->getNodeIdentifier();
                std::string connection_name = ni.getCodeName();

                if( ni == node_identifier )
                    values.push_back("me");
                else
                    values.push_back( NetworkManager::getStatusForConnection(connection_name) );

                table->addRow( values );

            }

            std::string title = au::str("Cluster %lu ( version %lu )"
                                        , cluster_information.getId()
                                        , cluster_information.getVersion()
                                        );
            table->setDefaultTitle( title );
            nodes.clearVector();
        }

        return table;

    }

    std::vector<size_t> CommonNetwork::getDelilahIds()
    {
        return NetworkManager::getDelilahIds();
    }
    network::Collection* CommonNetwork::getConnectionsCollection( Visualization* visualization )
    {
        return NetworkManager::getConnectionsCollection(visualization);
    }
    size_t CommonNetwork::get_rate_in()
    {
        return NetworkManager::get_rate_in();
    }
    size_t CommonNetwork::get_rate_out()
    {
        return NetworkManager::get_rate_out();
    }

}
<|endoftext|>


<commit_before>// Copyright 2016 Alex Silva Torres
//
// 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 "cmd-exec.h"

#include <cstring>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/shm.h>

#include "interpreter/cmd-executor.h"

namespace seti {
namespace internal {

Arguments::Arguments(std::vector<std::string>&& args): args_(std::move(args)) {
  if (args_.size() > 1) {
    globbuf_.gl_offs = 1;
    int flag = GLOB_NOMAGIC | GLOB_MARK | GLOB_BRACE | GLOB_TILDE | GLOB_DOOFFS;

    for (int i = 1; i < args_.size(); i++) {
      if (i > 1) {
        flag |= GLOB_APPEND;
      }

      glob(args_[i].c_str(), flag, nullptr, &globbuf_);
    }

    globbuf_.gl_pathv[0] = const_cast<char*>(args_[0].c_str());

    // globbuf_.gl_pathc don't count the offset, so we need plus one
    // on the memory allocation, on the loop and in the nullptr assignment
    argv_ = new char*[globbuf_.gl_pathc + 2];

    for (int i = 0; i <= globbuf_.gl_pathc; i++) {
      argv_[i] = const_cast<char*>(globbuf_.gl_pathv[i]);
    }

    argv_[globbuf_.gl_pathc + 1] = nullptr;
    return;
  }

  argv_ = new char*[2];
  argv_[0] = const_cast<char*>(args_[0].c_str());
  argv_[1] = nullptr;
}

Arguments::~Arguments() {
  delete argv_;
  globfree (&globbuf_);
}

char **Arguments::argsv() {
  return argv_;
}

std::vector<std::string> Arguments::args() {
  return std::move(args_);
}

Process::Process(SymbolTableStack& sym_tab, std::vector<std::string>&& args)
    : args_(std::move(args)), sym_tab_(sym_tab.MainTable())
    , completed_(false)
    , stopped_(false) {}

Process::Process(const Process& p): sym_tab_(p.sym_tab_) {
  args_ = p.args_;
  pid_ = p.pid_;
  completed_ = p.completed_;
  stopped_ = p.stopped_;
  status_ = p.status_;
}

Process& Process::operator=(const Process& p) {
  args_ = p.args_;
  pid_ = p.pid_;
  completed_ = p.completed_;
  stopped_ = p.stopped_;
  status_ = p.status_;
  sym_tab_ = p.sym_tab_;

  return *this;
}

Process::Process(Process&& p) {
  args_ = std::move(p.args_);
  pid_ = p.pid_;
  completed_ = p.completed_;
  stopped_ = p.stopped_;
  status_ = p.status_;
  sym_tab_ = std::move(p.sym_tab_);
}

Process& Process::operator=(Process&& p) {
  args_ = std::move(p.args_);
  pid_ = p.pid_;
  completed_ = p.completed_;
  stopped_ = p.stopped_;
  status_ = p.status_;
  sym_tab_ = std::move(p.sym_tab_);

  return *this;
}

void Process::LaunchProcess(int infile, int outfile, int errfile, pid_t pgid,
                            bool foreground) {
  pid_t pid;
  int shell_terminal = EnvShell::instance()->shell_terminal();
  int shell_is_interactive = isatty(shell_terminal);

  if (shell_is_interactive) {
    // Put the process into the process group and give the process group
    // the terminal, if appropriate.
    // This has to be done both by the shell and in the individual
    // child processes because of potential race conditions.
    pid = getpid ();

    if (pgid == 0) {
      pgid = pid;
    }

    setpgid (pid, pgid);

    if (foreground) {
      // this part fix the problem of stuck on tcsetpgrp
      // sources: https://dev.haiku-os.org/ticket/3417
      // https://dev.haiku-os.org/attachment/ticket/3417/tcsetpgrp-test.c
      // buf afeter put this part, the status return wrong for the commands
      // this part must be understood better
      sigset_t set, oset;
      sigemptyset (&set);
      sigaddset (&set, SIGCHLD);
      sigaddset (&set, SIGTTOU);
      sigemptyset (&oset);
      sigprocmask (SIG_BLOCK, &set, &oset);
      tcsetpgrp(shell_terminal, pgid);
    }

    // Set the handling for job control signals back to the default
    signal (SIGINT, SIG_DFL);
    signal (SIGQUIT, SIG_DFL);
    signal (SIGTSTP, SIG_DFL);
    signal (SIGTTIN, SIG_DFL);
    signal (SIGTTOU, SIG_DFL);
    signal (SIGCHLD, SIG_DFL);
  }

  // set the standard input/output channels of the new process
  if (infile != STDIN_FILENO) {
    dup2 (infile, STDIN_FILENO);
    close (infile);
  }

  if (outfile != STDOUT_FILENO) {
    dup2 (outfile, STDOUT_FILENO);
    close (outfile);
  }

  if (errfile != STDERR_FILENO) {
    dup2 (errfile, STDERR_FILENO);
    close (errfile);
  }

  std::vector<std::string> args;

  if (sym_tab_.ExistsCmdAlias(args_[0])) {
    args = sym_tab_.GetCmdAlias(args_[0]);

    // append arguments
    for (size_t i = 1; i < args_.size(); i++) {
      args.push_back(args_[i]);
    }
  } else {
    // append arguments
    for (auto& e: args_) {
      args.push_back(e);
    }
  }

  CmdEntryPtr cmd = sym_tab_.LookupCmd(args[0]);

  CmdSharedError *err = (CmdSharedError*)
      shmat(EnvShell::instance()->shmid(), 0, 0);
  err->error = false;

  Arguments glob_args(std::move(args));

  if (cmd) {
    try {
      LaunchCmd(cmd, std::move(glob_args.args()));
      exit(0);
    } catch (RunTimeError& e) {
      // set error on memory region
      err->error = true;
      err->except_code = static_cast<int>(e.code_);
      err->err_code = 0;
      int len = e.msg().length();
      len = len >= SETI_CMD_SIZE_MAX? SETI_CMD_SIZE_MAX-1: len;
      memcpy(err->err_str, e.msg().c_str(), len);
      exit(-1);
    }
  }

  int p = 0;

  // Exec the new process
  execvp(glob_args.argsv()[0], glob_args.argsv());

  // set error on memory region
  err->error = true;
  err->except_code = static_cast<int>(RunTimeError::ErrorCode::INVALID_COMMAND);
  err->err_code = errno;
  int len = std::strlen(glob_args.argsv()[0]);
  len = len >= SETI_CMD_SIZE_MAX? SETI_CMD_SIZE_MAX-1: len;
  memcpy(err->err_str, glob_args.argsv()[0], len);

  // set \0 to end string
  err->err_str[len] = '\0';

  shmdt(err);
  exit(-1);
}

void Process::LaunchCmd(CmdEntryPtr cmd, std::vector<std::string>&& args) {
  cmd->Exec(parent_, std::move(args));
}

char** Process::FillArgv(const std::vector<std::string>& args) {
  char **argv;
  argv = new char*[args.size() + 1];

  for (int i = 0; i < args.size(); i++) {
    argv[i] = const_cast<char*>(args[i].data());
  }

  argv[args.size()] = NULL;

  return argv;
}

int Job::MarkProcessStatus(pid_t pid, int status) {
  if (pid > 0) {
    for (auto& p: process_) {
      if (p.pid_ == pid) {
        p.status_ = status;
        if (WIFSTOPPED (status)) {
          p.stopped_ = true;
        } else {
          p.completed_ = true;
        }
        return 0;
      }
    }
  } else if (pid == 0 || errno == ECHILD) {
    /* No processes ready to report.  */
    return -1;
  }

  return -1;
}

bool Job::JobIsStopped() {
  for (auto& p: process_) {
    if (!p.completed_ && !p.stopped_) {
      return false;
    }
  }

  return true;
}

bool Job::JobIsCompleted() {
  for (auto& p: process_) {
    if (!p.completed_) {
      return false;
    }
  }

  return true;
}

void Job::WaitForJob() {
  pid_t pid;
  int status;

  do {
    pid = waitpid (WAIT_ANY, &status, WUNTRACED);
  } while (!MarkProcessStatus(pid, status)
           && !JobIsStopped() && !JobIsCompleted());

  signal(SIGCHLD, SIG_IGN);
}

int Job::Status() {
  int status = 0;

  for (auto& p: process_) {
    status |= p.status_;
  }

  return status;
}

void Job::PutJobInForeground(int cont) {
  int shell_terminal = EnvShell::instance()->shell_terminal();
  struct termios* tmodes = EnvShell::instance()->shell_tmodes();
  pid_t shell_pgid = EnvShell::instance()->shell_pgid();

  // put the job into the foreground
  tcsetpgrp (shell_terminal, pgid_);

  // send the job a continue signal, if necessary
  if (cont) {
    tcsetattr (shell_terminal, TCSADRAIN, &shell_tmodes_);
    if (kill (- pgid_, SIGCONT) < 0) {
      perror ("kill (SIGCONT)");
    }
  }

  // wait for it to report
  WaitForJob();

  // put the shell back in the foreground
  tcsetpgrp(shell_terminal, shell_pgid);

  /* Restore the shell’s terminal modes.  */
  tcgetattr(shell_terminal, &shell_tmodes_);
  tcsetattr(shell_terminal, TCSADRAIN, tmodes);
}

void Job::PutJobInBackground(int cont) {
  /* Send the job a continue signal, if necessary.  */
  if (cont) {
    if (kill (-pgid_, SIGCONT) < 0)
      perror ("kill (SIGCONT)");
  }
}

void Job::LaunchInternalCmd(CmdEntryPtr cmd) {
  static_cast<CmdInEntry&>(*cmd).SetStdFd(stdout_, stderr_, stdin_);
  cmd->Exec(parent_, std::move(process_[0].args_));

  if (stdin_ != STDIN_FILENO) {
    close (stdin_);
  }

  if (stdout_ != STDOUT_FILENO) {
    close (stdout_);
  }

  if (stderr_ != STDERR_FILENO) {
    close (stderr_);
  }
}

void Job::LaunchJob(int foreground) {
  int shell_terminal = EnvShell::instance()->shell_terminal();

  // executes commands that change self process status
  // commands like cd and exit must be executed this way
  if (process_.size() == 1) {
    CmdEntryPtr cmd = sym_tab_.LookupCmd(process_[0].args_[0]);

    if (cmd) {
      if (cmd->type() == CmdEntry::Type::kIn) {
        LaunchInternalCmd(cmd);
        return;
      }
    }
  }

  pid_t pid;
  int mypipe[2], infile, outfile;

  infile = stdin_;
  for (size_t i = 0; i < process_.size(); i++) {
    // set up pipes, if necessary
    if (i != (process_.size() - 1)) {
      if (pipe (mypipe) < 0) {
        perror ("pipe");
        exit (1);
      }

      outfile = mypipe[1];
    }
    else {
      outfile = stdout_;
    }

    // fork the child processes
    pid = fork ();
    if (pid == 0) {
      // this is the child process
      process_[i].LaunchProcess(infile, outfile, stderr_, pgid_, foreground);
    } else if (pid < 0) {
      // the fork failed
      perror ("fork");
      exit (1);
    } else {
      // this is the parent process
      process_[i].pid_ = pid;
      int shell_is_interactive = isatty(shell_terminal);

      if (shell_is_interactive) {
        if (!pgid_) {
          pgid_ = pid;
        }

        setpgid(pid, pgid_);
      }
    }

    // clean up after pipes
    if (infile != stdin_)
      close (infile);
    if (outfile != stdout_)
      close (outfile);
    infile = mypipe[0];
  }

  int shell_is_interactive = isatty(shell_terminal);

  if (!shell_is_interactive) {
    WaitForJob();
    CheckCmdError();
  } else if (foreground) {
    signal (SIGCHLD, SIG_DFL);
    PutJobInForeground(0);
    CheckCmdError();
  } else {
    PutJobInBackground(0);
  }
}

void Job::CheckCmdError() {
  // verify if there was some error executing command
  CmdSharedError *cmd_err = (CmdSharedError *) shmat(
        EnvShell::instance()->shmid(), 0, 0);

  CmdSharedError cpy_err;
  std::memcpy(&cpy_err, cmd_err, sizeof(CmdSharedError));

  shmdt(cmd_err);
  if (cpy_err.error) {
        throw RunTimeError(RunTimeError::ErrorCode::INVALID_COMMAND,
                           boost::format("%1%: %2%")%
                           cpy_err.err_str %strerror(cpy_err.err_code));
  }
}

}
}
<commit_msg>fix glob for buitin commands<commit_after>// Copyright 2016 Alex Silva Torres
//
// 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 "cmd-exec.h"

#include <cstring>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/shm.h>

#include "interpreter/cmd-executor.h"

namespace seti {
namespace internal {

Arguments::Arguments(std::vector<std::string>&& args): args_(std::move(args)) {
  if (args_.size() > 1) {
    globbuf_.gl_offs = 1;
    int flag = GLOB_NOMAGIC | GLOB_BRACE | GLOB_TILDE | GLOB_DOOFFS;

    for (int i = 1; i < args_.size(); i++) {
      if (i > 1) {
        flag |= GLOB_APPEND;
      }

      glob(args_[i].c_str(), flag, nullptr, &globbuf_);
    }

    globbuf_.gl_pathv[0] = const_cast<char*>(args_[0].c_str());

    // globbuf_.gl_pathc don't count the offset, so we need plus one
    // on the memory allocation, on the loop and in the nullptr assignment
    argv_ = new char*[globbuf_.gl_pathc + 2];

    for (int i = 0; i <= globbuf_.gl_pathc; i++) {
      argv_[i] = const_cast<char*>(globbuf_.gl_pathv[i]);
    }

    argv_[globbuf_.gl_pathc + 1] = nullptr;
    return;
  }

  argv_ = new char*[2];
  argv_[0] = const_cast<char*>(args_[0].c_str());
  argv_[1] = nullptr;
}

Arguments::~Arguments() {
  delete argv_;
  globfree (&globbuf_);
}

char **Arguments::argsv() {
  return argv_;
}

std::vector<std::string> Arguments::args() {
  std::vector<std::string> arg_vec;

  int p = 0;
  while (argv_[p]) {
    arg_vec.push_back(std::string(argv_[p++]));
  }

  return arg_vec;
}

Process::Process(SymbolTableStack& sym_tab, std::vector<std::string>&& args)
    : args_(std::move(args)), sym_tab_(sym_tab.MainTable())
    , completed_(false)
    , stopped_(false) {}

Process::Process(const Process& p): sym_tab_(p.sym_tab_) {
  args_ = p.args_;
  pid_ = p.pid_;
  completed_ = p.completed_;
  stopped_ = p.stopped_;
  status_ = p.status_;
}

Process& Process::operator=(const Process& p) {
  args_ = p.args_;
  pid_ = p.pid_;
  completed_ = p.completed_;
  stopped_ = p.stopped_;
  status_ = p.status_;
  sym_tab_ = p.sym_tab_;

  return *this;
}

Process::Process(Process&& p) {
  args_ = std::move(p.args_);
  pid_ = p.pid_;
  completed_ = p.completed_;
  stopped_ = p.stopped_;
  status_ = p.status_;
  sym_tab_ = std::move(p.sym_tab_);
}

Process& Process::operator=(Process&& p) {
  args_ = std::move(p.args_);
  pid_ = p.pid_;
  completed_ = p.completed_;
  stopped_ = p.stopped_;
  status_ = p.status_;
  sym_tab_ = std::move(p.sym_tab_);

  return *this;
}

void Process::LaunchProcess(int infile, int outfile, int errfile, pid_t pgid,
                            bool foreground) {
  pid_t pid;
  int shell_terminal = EnvShell::instance()->shell_terminal();
  int shell_is_interactive = isatty(shell_terminal);

  if (shell_is_interactive) {
    // Put the process into the process group and give the process group
    // the terminal, if appropriate.
    // This has to be done both by the shell and in the individual
    // child processes because of potential race conditions.
    pid = getpid ();

    if (pgid == 0) {
      pgid = pid;
    }

    setpgid (pid, pgid);

    if (foreground) {
      // this part fix the problem of stuck on tcsetpgrp
      // sources: https://dev.haiku-os.org/ticket/3417
      // https://dev.haiku-os.org/attachment/ticket/3417/tcsetpgrp-test.c
      // buf afeter put this part, the status return wrong for the commands
      // this part must be understood better
      sigset_t set, oset;
      sigemptyset (&set);
      sigaddset (&set, SIGCHLD);
      sigaddset (&set, SIGTTOU);
      sigemptyset (&oset);
      sigprocmask (SIG_BLOCK, &set, &oset);
      tcsetpgrp(shell_terminal, pgid);
    }

    // Set the handling for job control signals back to the default
    signal (SIGINT, SIG_DFL);
    signal (SIGQUIT, SIG_DFL);
    signal (SIGTSTP, SIG_DFL);
    signal (SIGTTIN, SIG_DFL);
    signal (SIGTTOU, SIG_DFL);
    signal (SIGCHLD, SIG_DFL);
  }

  // set the standard input/output channels of the new process
  if (infile != STDIN_FILENO) {
    dup2 (infile, STDIN_FILENO);
    close (infile);
  }

  if (outfile != STDOUT_FILENO) {
    dup2 (outfile, STDOUT_FILENO);
    close (outfile);
  }

  if (errfile != STDERR_FILENO) {
    dup2 (errfile, STDERR_FILENO);
    close (errfile);
  }

  std::vector<std::string> args;

  if (sym_tab_.ExistsCmdAlias(args_[0])) {
    args = sym_tab_.GetCmdAlias(args_[0]);

    // append arguments
    for (size_t i = 1; i < args_.size(); i++) {
      args.push_back(args_[i]);
    }
  } else {
    // append arguments
    for (auto& e: args_) {
      args.push_back(e);
    }
  }

  CmdEntryPtr cmd = sym_tab_.LookupCmd(args[0]);

  CmdSharedError *err = (CmdSharedError*)
      shmat(EnvShell::instance()->shmid(), 0, 0);
  err->error = false;

  Arguments glob_args(std::move(args));

  if (cmd) {
    try {
      LaunchCmd(cmd, std::move(glob_args.args()));
      exit(0);
    } catch (RunTimeError& e) {
      // set error on memory region
      err->error = true;
      err->except_code = static_cast<int>(e.code_);
      err->err_code = 0;
      int len = e.msg().length();
      len = len >= SETI_CMD_SIZE_MAX? SETI_CMD_SIZE_MAX-1: len;
      memcpy(err->err_str, e.msg().c_str(), len);
      exit(-1);
    }
  }

  int p = 0;

  // Exec the new process
  execvp(glob_args.argsv()[0], glob_args.argsv());

  // set error on memory region
  err->error = true;
  err->except_code = static_cast<int>(RunTimeError::ErrorCode::INVALID_COMMAND);
  err->err_code = errno;
  int len = std::strlen(glob_args.argsv()[0]);
  len = len >= SETI_CMD_SIZE_MAX? SETI_CMD_SIZE_MAX-1: len;
  memcpy(err->err_str, glob_args.argsv()[0], len);

  // set \0 to end string
  err->err_str[len] = '\0';

  shmdt(err);
  exit(-1);
}

void Process::LaunchCmd(CmdEntryPtr cmd, std::vector<std::string>&& args) {
  cmd->Exec(parent_, std::move(args));
}

char** Process::FillArgv(const std::vector<std::string>& args) {
  char **argv;
  argv = new char*[args.size() + 1];

  for (int i = 0; i < args.size(); i++) {
    argv[i] = const_cast<char*>(args[i].data());
  }

  argv[args.size()] = NULL;

  return argv;
}

int Job::MarkProcessStatus(pid_t pid, int status) {
  if (pid > 0) {
    for (auto& p: process_) {
      if (p.pid_ == pid) {
        p.status_ = status;
        if (WIFSTOPPED (status)) {
          p.stopped_ = true;
        } else {
          p.completed_ = true;
        }
        return 0;
      }
    }
  } else if (pid == 0 || errno == ECHILD) {
    /* No processes ready to report.  */
    return -1;
  }

  return -1;
}

bool Job::JobIsStopped() {
  for (auto& p: process_) {
    if (!p.completed_ && !p.stopped_) {
      return false;
    }
  }

  return true;
}

bool Job::JobIsCompleted() {
  for (auto& p: process_) {
    if (!p.completed_) {
      return false;
    }
  }

  return true;
}

void Job::WaitForJob() {
  pid_t pid;
  int status;

  do {
    pid = waitpid (WAIT_ANY, &status, WUNTRACED);
  } while (!MarkProcessStatus(pid, status)
           && !JobIsStopped() && !JobIsCompleted());

  signal(SIGCHLD, SIG_IGN);
}

int Job::Status() {
  int status = 0;

  for (auto& p: process_) {
    status |= p.status_;
  }

  return status;
}

void Job::PutJobInForeground(int cont) {
  int shell_terminal = EnvShell::instance()->shell_terminal();
  struct termios* tmodes = EnvShell::instance()->shell_tmodes();
  pid_t shell_pgid = EnvShell::instance()->shell_pgid();

  // put the job into the foreground
  tcsetpgrp (shell_terminal, pgid_);

  // send the job a continue signal, if necessary
  if (cont) {
    tcsetattr (shell_terminal, TCSADRAIN, &shell_tmodes_);
    if (kill (- pgid_, SIGCONT) < 0) {
      perror ("kill (SIGCONT)");
    }
  }

  // wait for it to report
  WaitForJob();

  // put the shell back in the foreground
  tcsetpgrp(shell_terminal, shell_pgid);

  /* Restore the shell’s terminal modes.  */
  tcgetattr(shell_terminal, &shell_tmodes_);
  tcsetattr(shell_terminal, TCSADRAIN, tmodes);
}

void Job::PutJobInBackground(int cont) {
  /* Send the job a continue signal, if necessary.  */
  if (cont) {
    if (kill (-pgid_, SIGCONT) < 0)
      perror ("kill (SIGCONT)");
  }
}

void Job::LaunchInternalCmd(CmdEntryPtr cmd) {
  static_cast<CmdInEntry&>(*cmd).SetStdFd(stdout_, stderr_, stdin_);

  Arguments glob_args(std::move(process_[0].args_));

  cmd->Exec(parent_, std::move(glob_args.args()));

  if (stdin_ != STDIN_FILENO) {
    close (stdin_);
  }

  if (stdout_ != STDOUT_FILENO) {
    close (stdout_);
  }

  if (stderr_ != STDERR_FILENO) {
    close (stderr_);
  }
}

void Job::LaunchJob(int foreground) {
  int shell_terminal = EnvShell::instance()->shell_terminal();

  // executes commands that change self process status
  // commands like cd and exit must be executed this way
  if (process_.size() == 1) {
    CmdEntryPtr cmd = sym_tab_.LookupCmd(process_[0].args_[0]);

    if (cmd) {
      if (cmd->type() == CmdEntry::Type::kIn) {
        LaunchInternalCmd(cmd);
        return;
      }
    }
  }

  pid_t pid;
  int mypipe[2], infile, outfile;

  infile = stdin_;
  for (size_t i = 0; i < process_.size(); i++) {
    // set up pipes, if necessary
    if (i != (process_.size() - 1)) {
      if (pipe (mypipe) < 0) {
        perror ("pipe");
        exit (1);
      }

      outfile = mypipe[1];
    }
    else {
      outfile = stdout_;
    }

    // fork the child processes
    pid = fork ();
    if (pid == 0) {
      // this is the child process
      process_[i].LaunchProcess(infile, outfile, stderr_, pgid_, foreground);
    } else if (pid < 0) {
      // the fork failed
      perror ("fork");
      exit (1);
    } else {
      // this is the parent process
      process_[i].pid_ = pid;
      int shell_is_interactive = isatty(shell_terminal);

      if (shell_is_interactive) {
        if (!pgid_) {
          pgid_ = pid;
        }

        setpgid(pid, pgid_);
      }
    }

    // clean up after pipes
    if (infile != stdin_)
      close (infile);
    if (outfile != stdout_)
      close (outfile);
    infile = mypipe[0];
  }

  int shell_is_interactive = isatty(shell_terminal);

  if (!shell_is_interactive) {
    WaitForJob();
    CheckCmdError();
  } else if (foreground) {
    signal (SIGCHLD, SIG_DFL);
    PutJobInForeground(0);
    CheckCmdError();
  } else {
    PutJobInBackground(0);
  }
}

void Job::CheckCmdError() {
  // verify if there was some error executing command
  CmdSharedError *cmd_err = (CmdSharedError *) shmat(
        EnvShell::instance()->shmid(), 0, 0);

  CmdSharedError cpy_err;
  std::memcpy(&cpy_err, cmd_err, sizeof(CmdSharedError));

  shmdt(cmd_err);
  if (cpy_err.error) {
        throw RunTimeError(RunTimeError::ErrorCode::INVALID_COMMAND,
                           boost::format("%1%: %2%")%
                           cpy_err.err_str %strerror(cpy_err.err_code));
  }
}

}
}
<|endoftext|>


<commit_before>#include <stdio.h>
#include <string.h>
#include <math.h>
#include "ui.hpp"

static void ui_draw_sidebar_background(UIState *s, bool hasSidebar) {
  int sbr_x = hasSidebar ? 0 : -(sbr_w) + bdr_s * 2;

  nvgBeginPath(s->vg);
  nvgRect(s->vg, sbr_x, 0, sbr_w, vwp_h);
  nvgFillColor(s->vg, COLOR_BLACK_ALPHA);
  nvgFill(s->vg);
}

static void ui_draw_sidebar_settings_button(UIState *s, bool hasSidebar) {
  bool settingsActive = s->active_app == cereal_UiLayoutState_App_settings;
  const int settings_btn_xr = hasSidebar ? settings_btn_x : -(sbr_w);

  nvgBeginPath(s->vg);
  NVGpaint imgPaint = nvgImagePattern(s->vg, settings_btn_xr, settings_btn_y,
    settings_btn_w, settings_btn_h, 0, s->img_button_settings, settingsActive ? 1.0f : 0.65f);
  nvgRect(s->vg, settings_btn_xr, settings_btn_y, settings_btn_w, settings_btn_h);
  nvgFillPaint(s->vg, imgPaint);
  nvgFill(s->vg);
}

static void ui_draw_sidebar_home_button(UIState *s, bool hasSidebar) {
  bool homeActive = s->active_app == cereal_UiLayoutState_App_home;
  const int home_btn_xr = hasSidebar ? home_btn_x : -(sbr_w);

  nvgBeginPath(s->vg);
  NVGpaint imgPaint = nvgImagePattern(s->vg, home_btn_xr, home_btn_y,
    home_btn_w, home_btn_h, 0, s->img_button_home, homeActive ? 1.0f : 0.65f);
  nvgRect(s->vg, home_btn_xr, home_btn_y, home_btn_w, home_btn_h);
  nvgFillPaint(s->vg, imgPaint);
  nvgFill(s->vg);
}

static void ui_draw_sidebar_network_strength(UIState *s, bool hasSidebar) {
  const int network_img_h = 27;
  const int network_img_w = 176;
  const int network_img_x = hasSidebar ? 58 : -(sbr_w);
  const int network_img_y = 196;
  const int network_img = s->scene.networkType == cereal_ThermalData_NetworkType_none ?
                          s->img_network[0] : s->img_network[s->scene.networkStrength + 1];

  nvgBeginPath(s->vg);
  NVGpaint imgPaint = nvgImagePattern(s->vg, network_img_x, network_img_y,
    network_img_w, network_img_h, 0, network_img, 1.0f);
  nvgRect(s->vg, network_img_x, network_img_y, network_img_w, network_img_h);
  nvgFillPaint(s->vg, imgPaint);
  nvgFill(s->vg);
}

static void ui_draw_sidebar_battery_icon(UIState *s, bool hasSidebar) {
  const int battery_img_h = 36;
  const int battery_img_w = 76;
  const int battery_img_x = hasSidebar ? 160 : -(sbr_w);
  const int battery_img_y = 255;

  int battery_img = strcmp(s->scene.batteryStatus, "Charging") == 0 ?
    s->img_battery_charging : s->img_battery;

  nvgBeginPath(s->vg);
  nvgRect(s->vg, battery_img_x + 6, battery_img_y + 5,
    ((battery_img_w - 19) * (s->scene.batteryPercent * 0.01)), battery_img_h - 11);
  nvgFillColor(s->vg, COLOR_WHITE);
  nvgFill(s->vg);

  nvgBeginPath(s->vg);
  NVGpaint imgPaint = nvgImagePattern(s->vg, battery_img_x, battery_img_y,
    battery_img_w, battery_img_h, 0, battery_img, 1.0f);
  nvgRect(s->vg, battery_img_x, battery_img_y, battery_img_w, battery_img_h);
  nvgFillPaint(s->vg, imgPaint);
  nvgFill(s->vg);
}

static void ui_draw_sidebar_network_type(UIState *s, bool hasSidebar) {
  const int network_x = hasSidebar ? 50 : -(sbr_w);
  const int network_y = 273;
  const int network_w = 100;
  const int network_h = 100;
  const char *network_types[6] = {"--", "WiFi", "2G", "3G", "4G", "5G"};
  char network_type_str[32];

  if (s->scene.networkType <= 5) {
    snprintf(network_type_str, sizeof(network_type_str), "%s", network_types[s->scene.networkType]);
  }

  nvgFillColor(s->vg, COLOR_WHITE);
  nvgFontSize(s->vg, 48);
  nvgFontFace(s->vg, "sans-regular");
  nvgTextAlign(s->vg, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE);
  nvgTextBox(s->vg, network_x, network_y, network_w, network_type_str, NULL);
}

static void ui_draw_sidebar_metric(UIState *s, const char* label_str, const char* value_str, const int severity, const int y_offset, const char* message_str, bool hasSidebar) {
  const int metric_x = hasSidebar ? 30 : -(sbr_w);
  const int metric_y = 338 + y_offset;
  const int metric_w = 240;
  const int metric_h = message_str ? strlen(message_str) > 8 ? 124 : 100 : 148;
  NVGcolor status_color;

  if (severity == 0) {
    status_color = COLOR_WHITE;
  } else if (severity == 1) {
    status_color = COLOR_YELLOW;
  } else if (severity > 1) {
    status_color = COLOR_RED;
  }

  nvgBeginPath(s->vg);
  nvgRoundedRect(s->vg, metric_x, metric_y, metric_w, metric_h, 20);
  nvgStrokeColor(s->vg, severity > 0 ? COLOR_WHITE : COLOR_WHITE_ALPHA);
  nvgStrokeWidth(s->vg, 2);
  nvgStroke(s->vg);

  nvgBeginPath(s->vg);
  nvgRoundedRectVarying(s->vg, metric_x + 6, metric_y + 6, 18, metric_h - 12, 25, 0, 0, 25);
  nvgFillColor(s->vg, status_color);
  nvgFill(s->vg);

  if (!message_str) {
    nvgFillColor(s->vg, COLOR_WHITE);
    nvgFontSize(s->vg, 78);
    nvgFontFace(s->vg, "sans-bold");
    nvgTextAlign(s->vg, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE);
    nvgTextBox(s->vg, metric_x + 50, metric_y + 50, metric_w - 60, value_str, NULL);

    nvgFillColor(s->vg, COLOR_WHITE);
    nvgFontSize(s->vg, 48);
    nvgFontFace(s->vg, "sans-regular");
    nvgTextAlign(s->vg, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE);
    nvgTextBox(s->vg, metric_x + 50, metric_y + 50 + 66, metric_w - 60, label_str, NULL);
  } else {
    nvgFillColor(s->vg, COLOR_WHITE);
    nvgFontSize(s->vg, 48);
    nvgFontFace(s->vg, "sans-bold");
    nvgTextAlign(s->vg, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE);
    nvgTextBox(s->vg, metric_x + 35, metric_y + (strlen(message_str) > 8 ? 40 : 50), metric_w - 50, message_str, NULL);
  }
}

static void ui_draw_sidebar_temp_metric(UIState *s, bool hasSidebar) {
  int temp_severity;
  char temp_label_str[32];
  char temp_value_str[32];
  char temp_value_unit[32];
  const int temp_y_offset = 0;

  if (s->scene.thermalStatus == cereal_ThermalData_ThermalStatus_green) {
    temp_severity = 0;
  } else if (s->scene.thermalStatus == cereal_ThermalData_ThermalStatus_yellow) {
    temp_severity = 1;
  } else if (s->scene.thermalStatus == cereal_ThermalData_ThermalStatus_red) {
    temp_severity = 2;
  } else if (s->scene.thermalStatus == cereal_ThermalData_ThermalStatus_danger) {
    temp_severity = 3;
  }

  snprintf(temp_value_str, sizeof(temp_value_str), "%d", s->scene.paTemp);
  snprintf(temp_value_unit, sizeof(temp_value_unit), "%s", "°C");
  snprintf(temp_label_str, sizeof(temp_label_str), "%s", "TEMP");
  strcat(temp_value_str, temp_value_unit);

  ui_draw_sidebar_metric(s, temp_label_str, temp_value_str, temp_severity, temp_y_offset, NULL, hasSidebar);
}

static void ui_draw_sidebar_panda_metric(UIState *s, bool hasSidebar) {
  int panda_severity;
  char panda_message_str[32];
  const int panda_y_offset = 32 + 148;

  if (s->scene.hwType == cereal_HealthData_HwType_unknown) {
    panda_severity = 2;
    snprintf(panda_message_str, sizeof(panda_message_str), "%s", "NO PANDA");
  } else if (s->scene.hwType == cereal_HealthData_HwType_whitePanda) {
    panda_severity = 0;
    snprintf(panda_message_str, sizeof(panda_message_str), "%s", "PANDA ACTIVE");
  } else if (
      (s->scene.hwType == cereal_HealthData_HwType_greyPanda) ||
      (s->scene.hwType == cereal_HealthData_HwType_blackPanda) ||
      (s->scene.hwType == cereal_HealthData_HwType_uno)) {
      if (s->scene.satelliteCount == -1) {
        panda_severity = 0;
        snprintf(panda_message_str, sizeof(panda_message_str), "%s", "PANDA ACTIVE");
      } else if (s->scene.satelliteCount < 6) {
        panda_severity = 1;
        snprintf(panda_message_str, sizeof(panda_message_str), "%s", "PANDA\nNO GPS");
      } else if (s->scene.satelliteCount >= 6) {
        panda_severity = 0;
        snprintf(panda_message_str, sizeof(panda_message_str), "%s", "PANDA GOOD GPS");
      }
  }

  ui_draw_sidebar_metric(s, NULL, NULL, panda_severity, panda_y_offset, panda_message_str, hasSidebar);
}

void ui_draw_sidebar(UIState *s) {
  bool hasSidebar = !s->scene.uilayout_sidebarcollapsed;
  ui_draw_sidebar_background(s, hasSidebar);
  ui_draw_sidebar_settings_button(s, hasSidebar);
  ui_draw_sidebar_home_button(s, hasSidebar);
  ui_draw_sidebar_network_strength(s, hasSidebar);
  ui_draw_sidebar_battery_icon(s, hasSidebar);
  ui_draw_sidebar_network_type(s, hasSidebar);
  ui_draw_sidebar_temp_metric(s, hasSidebar);
  ui_draw_sidebar_panda_metric(s, hasSidebar);
}
<commit_msg>fix multiline strings in ui_draw_sidebar_metric<commit_after>#include <stdio.h>
#include <string.h>
#include <math.h>
#include "ui.hpp"

static void ui_draw_sidebar_background(UIState *s, bool hasSidebar) {
  int sbr_x = hasSidebar ? 0 : -(sbr_w) + bdr_s * 2;

  nvgBeginPath(s->vg);
  nvgRect(s->vg, sbr_x, 0, sbr_w, vwp_h);
  nvgFillColor(s->vg, COLOR_BLACK_ALPHA);
  nvgFill(s->vg);
}

static void ui_draw_sidebar_settings_button(UIState *s, bool hasSidebar) {
  bool settingsActive = s->active_app == cereal_UiLayoutState_App_settings;
  const int settings_btn_xr = hasSidebar ? settings_btn_x : -(sbr_w);

  nvgBeginPath(s->vg);
  NVGpaint imgPaint = nvgImagePattern(s->vg, settings_btn_xr, settings_btn_y,
    settings_btn_w, settings_btn_h, 0, s->img_button_settings, settingsActive ? 1.0f : 0.65f);
  nvgRect(s->vg, settings_btn_xr, settings_btn_y, settings_btn_w, settings_btn_h);
  nvgFillPaint(s->vg, imgPaint);
  nvgFill(s->vg);
}

static void ui_draw_sidebar_home_button(UIState *s, bool hasSidebar) {
  bool homeActive = s->active_app == cereal_UiLayoutState_App_home;
  const int home_btn_xr = hasSidebar ? home_btn_x : -(sbr_w);

  nvgBeginPath(s->vg);
  NVGpaint imgPaint = nvgImagePattern(s->vg, home_btn_xr, home_btn_y,
    home_btn_w, home_btn_h, 0, s->img_button_home, homeActive ? 1.0f : 0.65f);
  nvgRect(s->vg, home_btn_xr, home_btn_y, home_btn_w, home_btn_h);
  nvgFillPaint(s->vg, imgPaint);
  nvgFill(s->vg);
}

static void ui_draw_sidebar_network_strength(UIState *s, bool hasSidebar) {
  const int network_img_h = 27;
  const int network_img_w = 176;
  const int network_img_x = hasSidebar ? 58 : -(sbr_w);
  const int network_img_y = 196;
  const int network_img = s->scene.networkType == cereal_ThermalData_NetworkType_none ?
                          s->img_network[0] : s->img_network[s->scene.networkStrength + 1];

  nvgBeginPath(s->vg);
  NVGpaint imgPaint = nvgImagePattern(s->vg, network_img_x, network_img_y,
    network_img_w, network_img_h, 0, network_img, 1.0f);
  nvgRect(s->vg, network_img_x, network_img_y, network_img_w, network_img_h);
  nvgFillPaint(s->vg, imgPaint);
  nvgFill(s->vg);
}

static void ui_draw_sidebar_battery_icon(UIState *s, bool hasSidebar) {
  const int battery_img_h = 36;
  const int battery_img_w = 76;
  const int battery_img_x = hasSidebar ? 160 : -(sbr_w);
  const int battery_img_y = 255;

  int battery_img = strcmp(s->scene.batteryStatus, "Charging") == 0 ?
    s->img_battery_charging : s->img_battery;

  nvgBeginPath(s->vg);
  nvgRect(s->vg, battery_img_x + 6, battery_img_y + 5,
    ((battery_img_w - 19) * (s->scene.batteryPercent * 0.01)), battery_img_h - 11);
  nvgFillColor(s->vg, COLOR_WHITE);
  nvgFill(s->vg);

  nvgBeginPath(s->vg);
  NVGpaint imgPaint = nvgImagePattern(s->vg, battery_img_x, battery_img_y,
    battery_img_w, battery_img_h, 0, battery_img, 1.0f);
  nvgRect(s->vg, battery_img_x, battery_img_y, battery_img_w, battery_img_h);
  nvgFillPaint(s->vg, imgPaint);
  nvgFill(s->vg);
}

static void ui_draw_sidebar_network_type(UIState *s, bool hasSidebar) {
  const int network_x = hasSidebar ? 50 : -(sbr_w);
  const int network_y = 273;
  const int network_w = 100;
  const int network_h = 100;
  const char *network_types[6] = {"--", "WiFi", "2G", "3G", "4G", "5G"};
  char network_type_str[32];

  if (s->scene.networkType <= 5) {
    snprintf(network_type_str, sizeof(network_type_str), "%s", network_types[s->scene.networkType]);
  }

  nvgFillColor(s->vg, COLOR_WHITE);
  nvgFontSize(s->vg, 48);
  nvgFontFace(s->vg, "sans-regular");
  nvgTextAlign(s->vg, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE);
  nvgTextBox(s->vg, network_x, network_y, network_w, network_type_str, NULL);
}

static void ui_draw_sidebar_metric(UIState *s, const char* label_str, const char* value_str, const int severity, const int y_offset, const char* message_str, bool hasSidebar) {
  const int metric_x = hasSidebar ? 30 : -(sbr_w);
  const int metric_y = 338 + y_offset;
  const int metric_w = 240;
  const int metric_h = message_str ? strchr(message_str, '\n') ? 124 : 100 : 148;

  NVGcolor status_color;

  if (severity == 0) {
    status_color = COLOR_WHITE;
  } else if (severity == 1) {
    status_color = COLOR_YELLOW;
  } else if (severity > 1) {
    status_color = COLOR_RED;
  }

  nvgBeginPath(s->vg);
  nvgRoundedRect(s->vg, metric_x, metric_y, metric_w, metric_h, 20);
  nvgStrokeColor(s->vg, severity > 0 ? COLOR_WHITE : COLOR_WHITE_ALPHA);
  nvgStrokeWidth(s->vg, 2);
  nvgStroke(s->vg);

  nvgBeginPath(s->vg);
  nvgRoundedRectVarying(s->vg, metric_x + 6, metric_y + 6, 18, metric_h - 12, 25, 0, 0, 25);
  nvgFillColor(s->vg, status_color);
  nvgFill(s->vg);

  if (!message_str) {
    nvgFillColor(s->vg, COLOR_WHITE);
    nvgFontSize(s->vg, 78);
    nvgFontFace(s->vg, "sans-bold");
    nvgTextAlign(s->vg, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE);
    nvgTextBox(s->vg, metric_x + 50, metric_y + 50, metric_w - 60, value_str, NULL);

    nvgFillColor(s->vg, COLOR_WHITE);
    nvgFontSize(s->vg, 48);
    nvgFontFace(s->vg, "sans-regular");
    nvgTextAlign(s->vg, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE);
    nvgTextBox(s->vg, metric_x + 50, metric_y + 50 + 66, metric_w - 60, label_str, NULL);
  } else {
    nvgFillColor(s->vg, COLOR_WHITE);
    nvgFontSize(s->vg, 48);
    nvgFontFace(s->vg, "sans-bold");
    nvgTextAlign(s->vg, NVG_ALIGN_CENTER | NVG_ALIGN_MIDDLE);
    nvgTextBox(s->vg, metric_x + 35, metric_y + (strchr(message_str, '\n') ? 40 : 50), metric_w - 50, message_str, NULL);
  }
}

static void ui_draw_sidebar_temp_metric(UIState *s, bool hasSidebar) {
  int temp_severity;
  char temp_label_str[32];
  char temp_value_str[32];
  char temp_value_unit[32];
  const int temp_y_offset = 0;

  if (s->scene.thermalStatus == cereal_ThermalData_ThermalStatus_green) {
    temp_severity = 0;
  } else if (s->scene.thermalStatus == cereal_ThermalData_ThermalStatus_yellow) {
    temp_severity = 1;
  } else if (s->scene.thermalStatus == cereal_ThermalData_ThermalStatus_red) {
    temp_severity = 2;
  } else if (s->scene.thermalStatus == cereal_ThermalData_ThermalStatus_danger) {
    temp_severity = 3;
  }

  snprintf(temp_value_str, sizeof(temp_value_str), "%d", s->scene.paTemp);
  snprintf(temp_value_unit, sizeof(temp_value_unit), "%s", "°C");
  snprintf(temp_label_str, sizeof(temp_label_str), "%s", "TEMP");
  strcat(temp_value_str, temp_value_unit);

  ui_draw_sidebar_metric(s, temp_label_str, temp_value_str, temp_severity, temp_y_offset, NULL, hasSidebar);
}

static void ui_draw_sidebar_panda_metric(UIState *s, bool hasSidebar) {
  int panda_severity;
  char panda_message_str[32];
  const int panda_y_offset = 32 + 148;

  if (s->scene.hwType == cereal_HealthData_HwType_unknown) {
    panda_severity = 2;
    snprintf(panda_message_str, sizeof(panda_message_str), "%s", "NO\nPANDA");
  } else if (s->scene.hwType == cereal_HealthData_HwType_whitePanda) {
    panda_severity = 0;
    snprintf(panda_message_str, sizeof(panda_message_str), "%s", "PANDA\nACTIVE");
  } else if (
      (s->scene.hwType == cereal_HealthData_HwType_greyPanda) ||
      (s->scene.hwType == cereal_HealthData_HwType_blackPanda) ||
      (s->scene.hwType == cereal_HealthData_HwType_uno)) {
      if (s->scene.satelliteCount == -1) {
        panda_severity = 0;
        snprintf(panda_message_str, sizeof(panda_message_str), "%s", "PANDA\nACTIVE");
      } else if (s->scene.satelliteCount < 6) {
        panda_severity = 1;
        snprintf(panda_message_str, sizeof(panda_message_str), "%s", "PANDA\nNO GPS");
      } else if (s->scene.satelliteCount >= 6) {
        panda_severity = 0;
        snprintf(panda_message_str, sizeof(panda_message_str), "%s", "PANDA\nGOOD GPS");
      }
  }

  ui_draw_sidebar_metric(s, NULL, NULL, panda_severity, panda_y_offset, panda_message_str, hasSidebar);
}

void ui_draw_sidebar(UIState *s) {
  bool hasSidebar = !s->scene.uilayout_sidebarcollapsed;
  ui_draw_sidebar_background(s, hasSidebar);
  ui_draw_sidebar_settings_button(s, hasSidebar);
  ui_draw_sidebar_home_button(s, hasSidebar);
  ui_draw_sidebar_network_strength(s, hasSidebar);
  ui_draw_sidebar_battery_icon(s, hasSidebar);
  ui_draw_sidebar_network_type(s, hasSidebar);
  ui_draw_sidebar_temp_metric(s, hasSidebar);
  ui_draw_sidebar_panda_metric(s, hasSidebar);
}
<|endoftext|>