<commit_before>/*
 * Copyright 2009 The VOTCA Development Team (http://www.votca.org)
 *
 * 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 <votca/tools/spline.h>
#include <votca/tools/cubicspline.h>
#include <votca/tools/akimaspline.h>
#include <votca/tools/linspline.h>
#include <votca/tools/table.h>
#include <votca/tools/tokenizer.h>
#include <boost/program_options.hpp>
#include <iostream>
#include "version.h"

using namespace std;
namespace po = boost::program_options;
using namespace votca::csg;
using namespace votca::tools;

void help_text()
{
    votca::csg::HelpTextHeader("csg_resample");
    cout << "Change grid + interval of any sort of table files.\n"
            "Mainly called internally by inverse script, can also be\n"
            "used to manually prepare input files for coarse-grained\n"
            "simulations.\n\n";
}

void check_option(po::options_description &desc, po::variables_map &vm, const string &option)
{
    if(!vm.count(option)) {
        cout << "csg_resample \n\n";
        cout << desc << endl << "parameter " << option << " is not specified\n";
        exit(1);
    }
}

int main(int argc, char** argv)
{

    string in_file, out_file, grid, fitgrid, comment, type, boundaries;
    Spline *spline;
    Table in, out, der;
    // program options
    po::options_description desc("Allowed options");

    desc.add_options()
      ("in", po::value<string>(&in_file), "table to read")
      ("out", po::value<string>(&out_file), "table to write")
      ("derivative", po::value<string>(), "table to write")
      ("grid", po::value<string>(&grid), "new grid spacing (min:step:max). If 'grid' is specified only, interpolation is performed.")
      ("type", po::value<string>(&type)->default_value("akima"), "[cubic|akima|linear]. If option is not specified, the default type 'akima' is assumed.")
      ("fitgrid", po::value<string>(&fitgrid), "specify fit grid (min:step:max). If 'grid' and 'fitgrid' are specified, a fit is performed.")
      ("nocut", "Option for fitgrid: Normally, values out of fitgrid boundaries are cut off. If they shouldn't, choose --nocut.")
      ("comment", po::value<string>(&comment), "store a comment in the output table")
      ("boundaries", po::value<string>(&boundaries), "(natural|periodic|derivativezero) sets boundary conditions")
      ("help", "options file for coarse graining");

    po::variables_map vm;
    try {
        po::store(po::parse_command_line(argc, argv, desc), vm);
        po::notify(vm);
    }
    catch(po::error err) {
        cout << "error parsing command line: " << err.what() << endl;
        return -1;
    }

    // does the user want help?
    if (vm.count("help")) {
        help_text();
        cout << desc << endl;
        return 0;
    }

    check_option(desc, vm, "in");
    check_option(desc, vm, "out");

    if(!(vm.count("grid") || vm.count("fitgrid"))) {
            cout << "Need grid for interpolation or fitgrid for fit.\n";
            return 1;
    }

    if((!vm.count("grid")) && vm.count("fitgrid")) {
            cout << "Need a grid for fitting as well.\n";
            return 1;
    }

    double min, max, step;
    {
        Tokenizer tok(grid, ":");
        vector<string> toks;
        tok.ToVector(toks);
        if(toks.size()!=3) {
            cout << "wrong range format, use min:step:max\n";
            return 1;
        }
        min = boost::lexical_cast<double>(toks[0]);
        step = boost::lexical_cast<double>(toks[1]);
        max = boost::lexical_cast<double>(toks[2]);
    }

    in.Load(in_file);

    if (vm.count("type")) {
        if(type=="cubic") {
            spline = new CubicSpline();
        }
        else if(type=="akima") {
            spline = new AkimaSpline();
        }
        else if(type=="linear") {
            spline = new LinSpline();
        }
        else {
            throw std::runtime_error("unknown type");
        }
    }
    spline->setBC(Spline::splineNormal);

    if (vm.count("boundaries")) {
        if(boundaries=="periodic") {
            spline->setBC(Spline::splinePeriodic);
        }
        if(boundaries=="derivativezero") {
            spline->setBC(Spline::splineDerivativeZero);
        }
        //default: normal
    }

    // in case fit is specified
    if (vm.count("fitgrid")) {
        Tokenizer tok(fitgrid, ":");
        vector<string> toks;
        tok.ToVector(toks);
        if(toks.size()!=3) {
            cout << "wrong range format in fitgrid, use min:step:max\n";
            return 1;
        }
        double sp_min, sp_max, sp_step;
        sp_min = boost::lexical_cast<double>(toks[0]);
        sp_step = boost::lexical_cast<double>(toks[1]);
        sp_max = boost::lexical_cast<double>(toks[2]);
        cout << "doing " << type << " fit " << sp_min << ":" << sp_step << ":" << sp_max << endl;

        // cut off any values out of fitgrid boundaries (exception: do nothing in case of --nocut)
        ub::vector<double> x_copy;
        ub::vector<double> y_copy;
        if (!vm.count("nocut")) {
            // determine vector size
            int minindex=-1, maxindex;
            for (int i=0; i<in.x().size(); i++) {
                if(in.x(i)<sp_min) {
                    minindex = i;
                }
                if(in.x(i)<sp_max) {
                    maxindex = i;
                }
            }
            // copy data values in [sp_min,sp_max] into new vectors
            minindex++;
            x_copy = ub::zero_vector<double>(maxindex-minindex+1);
            y_copy = ub::zero_vector<double>(maxindex-minindex+1);
            for (int i=minindex; i<=maxindex; i++) {
                x_copy(i-minindex) = in.x(i);
                y_copy(i-minindex) = in.y(i);
            }
        }

        // fitting
        spline->GenerateGrid(sp_min, sp_max, sp_step);
        if (vm.count("nocut")) {
            spline->Fit(in.x(), in.y());
        } else {
            spline->Fit(x_copy, y_copy);
        }
    } else {
        // otherwise do interpolation (default = cubic)
        spline->Interpolate(in.x(), in.y());
    }

    out.GenerateGridSpacing(min, max, step);
    spline->Calculate(out.x(), out.y());

    //store a comment line
    if (vm.count("comment")){
        out.set_comment(comment);
    }
    out.y() = out.y();
    out.flags() = ub::scalar_vector<double>(out.flags().size(), 'o');

    int i=0;
    for(i=0; out.x(i) < in.x(0) && i<out.size(); ++i);

    int j=0;
    for(;i < out.size(); ++i) {
        for(; j < in.size(); ++j)
            if(in.x(j) >= out.x(i)  || fabs(in.x(j)-out.x(i) ) < 1e-12) // fix for precison errors
                break;
        if(in.size() == j) break;
        out.flags(i) = in.flags(j);
    }

    out.Save(out_file);

    if (vm.count("derivative")) {
        der.GenerateGridSpacing(min, max, step);
        der.flags() = ub::scalar_vector<double>(der.flags().size(), 'o');

        spline->CalculateDerivative(der.x(), der.y());

        der.Save(vm["derivative"].as<string>());
    }

    delete spline;
    return 0;
}

<commit_msg>error handling in csg_resample<commit_after>/*
 * Copyright 2009 The VOTCA Development Team (http://www.votca.org)
 *
 * 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 <votca/tools/spline.h>
#include <votca/tools/cubicspline.h>
#include <votca/tools/akimaspline.h>
#include <votca/tools/linspline.h>
#include <votca/tools/table.h>
#include <votca/tools/tokenizer.h>
#include <boost/program_options.hpp>
#include <iostream>
#include "version.h"

using namespace std;
namespace po = boost::program_options;
using namespace votca::csg;
using namespace votca::tools;

void help_text()
{
    votca::csg::HelpTextHeader("csg_resample");
    cout << "Change grid + interval of any sort of table files.\n"
            "Mainly called internally by inverse script, can also be\n"
            "used to manually prepare input files for coarse-grained\n"
            "simulations.\n\n";
}

void check_option(po::options_description &desc, po::variables_map &vm, const string &option)
{
    if(!vm.count(option)) {
        cout << "csg_resample \n\n";
        cout << desc << endl << "parameter " << option << " is not specified\n";
        exit(1);
    }
}

int main(int argc, char** argv)
{

    string in_file, out_file, grid, fitgrid, comment, type, boundaries;
    Spline *spline;
    Table in, out, der;
    // program options
    po::options_description desc("Allowed options");

    desc.add_options()
      ("in", po::value<string>(&in_file), "table to read")
      ("out", po::value<string>(&out_file), "table to write")
      ("derivative", po::value<string>(), "table to write")
      ("grid", po::value<string>(&grid), "new grid spacing (min:step:max). If 'grid' is specified only, interpolation is performed.")
      ("type", po::value<string>(&type)->default_value("akima"), "[cubic|akima|linear]. If option is not specified, the default type 'akima' is assumed.")
      ("fitgrid", po::value<string>(&fitgrid), "specify fit grid (min:step:max). If 'grid' and 'fitgrid' are specified, a fit is performed.")
      ("nocut", "Option for fitgrid: Normally, values out of fitgrid boundaries are cut off. If they shouldn't, choose --nocut.")
      ("comment", po::value<string>(&comment), "store a comment in the output table")
      ("boundaries", po::value<string>(&boundaries), "(natural|periodic|derivativezero) sets boundary conditions")
      ("help", "options file for coarse graining");

    po::variables_map vm;
    try {
        po::store(po::parse_command_line(argc, argv, desc), vm);
        po::notify(vm);
    }
    catch(po::error err) {
        cout << "error parsing command line: " << err.what() << endl;
        return -1;
    }

    // does the user want help?
    if (vm.count("help")) {
        help_text();
        cout << desc << endl;
        return 0;
    }

    check_option(desc, vm, "in");
    check_option(desc, vm, "out");

    if(!(vm.count("grid") || vm.count("fitgrid"))) {
            cout << "Need grid for interpolation or fitgrid for fit.\n";
            return 1;
    }

    if((!vm.count("grid")) && vm.count("fitgrid")) {
            cout << "Need a grid for fitting as well.\n";
            return 1;
    }

    double min, max, step;
    {
        Tokenizer tok(grid, ":");
        vector<string> toks;
        tok.ToVector(toks);
        if(toks.size()!=3) {
            cout << "wrong range format, use min:step:max\n";
            return 1;
        }
        min = boost::lexical_cast<double>(toks[0]);
        step = boost::lexical_cast<double>(toks[1]);
        max = boost::lexical_cast<double>(toks[2]);
    }

    in.Load(in_file);

    if (vm.count("type")) {
        if(type=="cubic") {
            spline = new CubicSpline();
        }
        else if(type=="akima") {
            spline = new AkimaSpline();
        }
        else if(type=="linear") {
            spline = new LinSpline();
        }
        else {
            throw std::runtime_error("unknown type");
        }
    }
    spline->setBC(Spline::splineNormal);

    if (vm.count("boundaries")) {
        if(boundaries=="periodic") {
            spline->setBC(Spline::splinePeriodic);
        }
        if(boundaries=="derivativezero") {
            spline->setBC(Spline::splineDerivativeZero);
        }
        //default: normal
    }

    // in case fit is specified
    if (vm.count("fitgrid")) {
        Tokenizer tok(fitgrid, ":");
        vector<string> toks;
        tok.ToVector(toks);
        if(toks.size()!=3) {
            cout << "wrong range format in fitgrid, use min:step:max\n";
            return 1;
        }
        double sp_min, sp_max, sp_step;
        sp_min = boost::lexical_cast<double>(toks[0]);
        sp_step = boost::lexical_cast<double>(toks[1]);
        sp_max = boost::lexical_cast<double>(toks[2]);
        cout << "doing " << type << " fit " << sp_min << ":" << sp_step << ":" << sp_max << endl;

        // cut off any values out of fitgrid boundaries (exception: do nothing in case of --nocut)
        ub::vector<double> x_copy;
        ub::vector<double> y_copy;
        if (!vm.count("nocut")) {
            // determine vector size
            int minindex=-1, maxindex;
            for (int i=0; i<in.x().size(); i++) {
                if(in.x(i)<sp_min) {
                    minindex = i;
                }
                if(in.x(i)<sp_max) {
                    maxindex = i;
                }
            }
            // copy data values in [sp_min,sp_max] into new vectors
            minindex++;
            x_copy = ub::zero_vector<double>(maxindex-minindex+1);
            y_copy = ub::zero_vector<double>(maxindex-minindex+1);
            for (int i=minindex; i<=maxindex; i++) {
                x_copy(i-minindex) = in.x(i);
                y_copy(i-minindex) = in.y(i);
            }
        }

        // fitting
        spline->GenerateGrid(sp_min, sp_max, sp_step);
        try {
            if (vm.count("nocut")) {
                spline->Fit(in.x(), in.y());
            } else {
                spline->Fit(x_copy, y_copy);
            }
        } catch (const char* message) {
            if(message="qrsolve_zero_column_in_matrix") {
                throw std::runtime_error("error in Linalg::linalg_qrsolve : Not enough data for fit, please adjust grid (zero row in fit matrix)");
            }
            else if(message="constrained_qrsolve_zero_column_in_matrix") {
                throw std::runtime_error("error in Linalg::linalg_constrained_qrsolve : Not enough data for fit, please adjust grid (zero row in fit matrix)");
            }
            else throw std::runtime_error("Unknown error in csg_resample while fitting.");
        }
    } else {
        // otherwise do interpolation (default = cubic)
        try {
            spline->Interpolate(in.x(), in.y());
        } catch (const char* message) {
            if(message="qrsolve_zero_column_in_matrix") {
                throw std::runtime_error("error in Linalg::linalg_qrsolve : Not enough data, please adjust grid (zero row in fit matrix)");
            }
            else if(message="constrained_qrsolve_zero_column_in_matrix") {
                throw std::runtime_error("error in Linalg::linalg_constrained_qrsolve : Not enough data, please adjust grid (zero row in fit matrix)");
            }
            else throw std::runtime_error("Unknown error in csg_resample while interpolating.");
        }
    }

    out.GenerateGridSpacing(min, max, step);
    spline->Calculate(out.x(), out.y());

    //store a comment line
    if (vm.count("comment")){
        out.set_comment(comment);
    }
    out.y() = out.y();
    out.flags() = ub::scalar_vector<double>(out.flags().size(), 'o');

    int i=0;
    for(i=0; out.x(i) < in.x(0) && i<out.size(); ++i);

    int j=0;
    for(;i < out.size(); ++i) {
        for(; j < in.size(); ++j)
            if(in.x(j) >= out.x(i)  || fabs(in.x(j)-out.x(i) ) < 1e-12) // fix for precison errors
                break;
        if(in.size() == j) break;
        out.flags(i) = in.flags(j);
    }

    out.Save(out_file);

    if (vm.count("derivative")) {
        der.GenerateGridSpacing(min, max, step);
        der.flags() = ub::scalar_vector<double>(der.flags().size(), 'o');

        spline->CalculateDerivative(der.x(), der.y());

        der.Save(vm["derivative"].as<string>());
    }

    delete spline;
    return 0;
}

<|endoftext|>


<commit_before>#ifndef TURBO_TOOLSET_INTRINSIC_HPP
#define TURBO_TOOLSET_INTRINSIC_HPP

#include <cstdint>
#include <limits>

namespace turbo {
namespace toolset {

constexpr int uint32_digits()
{
    return std::numeric_limits<std::uint32_t>::digits;
}

constexpr int uint64_digits()
{
    return std::numeric_limits<std::uint64_t>::digits;
}

constexpr std::uint32_t pow_2_to_31()
{
    return 1U << 31;
}

inline std::uint32_t count_leading_zero(std::uint32_t input)
{
#if defined( _WIN32) && defined(_MSC_VER)
    std::uint32_t result = 0U;
    return (_BitScanReverse(result&, input) == 0) ? uint32_digits() : uint32_digits() - result + 1U;
#elif defined(__GNUC__) || defined(__clang__)
    return (input == 0U) ? uint32_digits() : __builtin_clz(input);
#else
    std::uint32_t count = 0U;
    while (count < uint32_digits() && (input & pow_2_to_31()) != pow_2_to_31())
    {
	input = input << 1;
	++count;
    }
    return count;
#endif
}

inline std::uint64_t count_leading_zero(std::uint64_t input)
{
#if defined( _WIN32) && defined(_MSC_VER)
    std::uint64_t result = 0U;
    return (_BitScanReverse64(result&, input) == 0) ? uint64_digits() : uint64_digits() - result + 1U;
#else
    std::uint32_t high_result = count_leading_zero(static_cast<std::uint32_t>((input & 0xFFFFFFFF00000000) >> uint32_digits()));
    return (high_result != uint32_digits()) ? high_result : count_leading_zero(static_cast<std::uint32_t>(input & 0x00000000FFFFFFFF)) + uint32_digits();
#endif
}

} // namespace toolset
} // namespace turbo

#endif

<commit_msg>overloading count_leading_zero for std::uint8_t and std::uint16_t<commit_after>#ifndef TURBO_TOOLSET_INTRINSIC_HPP
#define TURBO_TOOLSET_INTRINSIC_HPP

#include <cstdint>
#include <limits>

namespace turbo {
namespace toolset {

constexpr int uint32_digits()
{
    return std::numeric_limits<std::uint32_t>::digits;
}

constexpr int uint64_digits()
{
    return std::numeric_limits<std::uint64_t>::digits;
}

constexpr std::uint32_t pow_2_to_31()
{
    return 1U << 31;
}

inline std::uint32_t count_leading_zero(std::uint32_t input)
{
#if defined( _WIN32) && defined(_MSC_VER)
    std::uint32_t result = 0U;
    return (_BitScanReverse(result&, input) == 0) ? uint32_digits() : uint32_digits() - result + 1U;
#elif defined(__GNUC__) || defined(__clang__)
    return (input == 0U) ? uint32_digits() : __builtin_clz(input);
#else
    std::uint32_t count = 0U;
    while (count < uint32_digits() && (input & pow_2_to_31()) != pow_2_to_31())
    {
	input = input << 1;
	++count;
    }
    return count;
#endif
}

inline std::uint64_t count_leading_zero(std::uint64_t input)
{
#if defined( _WIN32) && defined(_MSC_VER)
    std::uint64_t result = 0U;
    return (_BitScanReverse64(result&, input) == 0) ? uint64_digits() : uint64_digits() - result + 1U;
#else
    std::uint32_t high_result = count_leading_zero(static_cast<std::uint32_t>((input & 0xFFFFFFFF00000000) >> uint32_digits()));
    return (high_result != uint32_digits())
	    ? high_result
	    : count_leading_zero(static_cast<std::uint32_t>(input & 0x00000000FFFFFFFF)) + uint32_digits();
#endif
}

inline std::uint32_t count_leading_zero(std::uint8_t input)
{
    return count_leading_zero(static_cast<std::uint32_t>(input))
	    + std::numeric_limits<std::uint8_t>::digits
	    - std::numeric_limits<std::uint32_t>::digits;
}

inline std::uint32_t count_leading_zero(std::uint16_t input)
{
    return count_leading_zero(static_cast<std::uint32_t>(input))
	    + std::numeric_limits<std::uint16_t>::digits
	    - std::numeric_limits<std::uint32_t>::digits;
}

} // namespace toolset
} // namespace turbo

#endif

<|endoftext|>


<commit_before><commit_msg>include DYLD_LIBRARY_PATH on macOS for run/dbg<commit_after><|endoftext|>


<commit_before>#include "common/Clock.h"

#include "rgw_log.h"
#include "rgw_acl.h"
#include "rgw_access.h"

#define DOUT_SUBSYS rgw

static rgw_bucket log_bucket(RGW_LOG_POOL_NAME);

static void set_param_str(struct req_state *s, const char *name, string& str)
{
  const char *p = s->env->get(name);
  if (p)
    str = p;
}

string render_log_object_name(const string& format,
			      struct tm *dt, int64_t bucket_id, const string& bucket_name)
{
  string o;
  for (unsigned i=0; i<format.size(); i++) {
    if (format[i] == '%' && i+1 < format.size()) {
      i++;
      char buf[32];
      switch (format[i]) {
      case '%':
	strcpy(buf, "%");
	break;
      case 'Y':
	sprintf(buf, "%.4d", dt->tm_year + 1900);
	break;
      case 'y':
	sprintf(buf, "%.2d", dt->tm_year % 100);
	break;
      case 'm':
	sprintf(buf, "%.2d", dt->tm_mon + 1);
	break;
      case 'd':
	sprintf(buf, "%.2d", dt->tm_mday);
	break;
      case 'H':
	sprintf(buf, "%.2d", dt->tm_hour);
	break;
      case 'I':
	sprintf(buf, "%.2d", (dt->tm_hour % 12) + 1);
	break;
      case 'k':
	sprintf(buf, "%d", dt->tm_hour);
	break;
      case 'l':
	sprintf(buf, "%d", (dt->tm_hour % 12) + 1);
	break;
      case 'M':
	sprintf(buf, "%.2d", dt->tm_min);
	break;

      case 'i':
	sprintf(buf, "%lld", (long long)bucket_id);
	break;
      case 'n':
	o += bucket_name;
	continue;
      default:
	// unknown code
	sprintf(buf, "%%%c", format[i]);
	break;
      }
      o += buf;
      continue;
    }
    o += format[i];
  }
  return o;
}

int rgw_log_op(struct req_state *s)
{
  struct rgw_log_entry entry;
  uint64_t bucket_id;

  if (!s->should_log)
    return 0;

  if (!s->bucket_name) {
    dout(0) << "nothing to log for operation" << dendl;
    return -EINVAL;
  }
  if (s->err.ret == -ERR_NO_SUCH_BUCKET) {
    if (!g_conf->rgw_log_nonexistent_bucket) {
      dout(0) << "bucket " << s->bucket << " doesn't exist, not logging" << dendl;
      return 0;
    }
    bucket_id = 0;
  } else {
    bucket_id = s->bucket.bucket_id;
  }
  entry.bucket = s->bucket_name;

  if (s->object)
    entry.obj = s->object;
  else
    entry.obj = "-";

  entry.obj_size = s->obj_size;

  if (g_conf->rgw_remote_addr_param.length())
    set_param_str(s, g_conf->rgw_remote_addr_param.c_str(), entry.remote_addr);
  else
    set_param_str(s, "REMOTE_ADDR", entry.remote_addr);
  set_param_str(s, "HTTP_USER_AGENT", entry.user_agent);
  set_param_str(s, "HTTP_REFERRER", entry.referrer);
  set_param_str(s, "REQUEST_URI", entry.uri);
  set_param_str(s, "REQUEST_METHOD", entry.op);

  entry.user = s->user.user_id;
  if (s->acl)
    entry.object_owner = s->acl->get_owner().get_id();
  entry.bucket_owner = s->bucket_owner;

  entry.time = s->time;
  entry.total_time = ceph_clock_now(g_ceph_context) - s->time;
  entry.bytes_sent = s->bytes_sent;
  entry.bytes_received = s->bytes_received;
  if (s->err.http_ret) {
    char buf[16];
    snprintf(buf, sizeof(buf), "%d", s->err.http_ret);
    entry.http_status = buf;
  } else
    entry.http_status = "200"; // default

  entry.error_code = s->err.s3_code;
  entry.bucket_id = bucket_id;

  bufferlist bl;
  ::encode(entry, bl);

  struct tm bdt;
  time_t t = entry.time.sec();
  if (g_conf->rgw_log_object_name_utc)
    gmtime_r(&t, &bdt);
  else
    localtime_r(&t, &bdt);

  string oid = render_log_object_name(g_conf->rgw_log_object_name, &bdt,
				      s->bucket.bucket_id, entry.bucket.c_str());

  rgw_obj obj(log_bucket, oid);

  int ret = rgwstore->append_async(obj, bl.length(), bl);
  if (ret == -ENOENT) {
    string id;
    map<std::string, bufferlist> attrs;
    ret = rgwstore->create_bucket(id, log_bucket, attrs, true);
    if (ret < 0)
      goto done;
    // retry
    ret = rgwstore->append_async(obj, bl.length(), bl);
  }
done:
  if (ret < 0)
    dout(0) << "failed to log entry" << dendl;

  return ret;
}

int rgw_log_intent(struct req_state *s, rgw_obj& obj, RGWIntentEvent intent)
{
  rgw_bucket intent_log_bucket(RGW_INTENT_LOG_POOL_NAME);

  rgw_intent_log_entry entry;
  entry.obj = obj;
  entry.intent = (uint32_t)intent;
  entry.op_time = s->time;

  struct tm bdt;
  time_t t = entry.op_time.sec();
  if (g_conf->rgw_intent_log_object_name_utc)
    gmtime_r(&t, &bdt);
  else
    localtime_r(&t, &bdt);

  char buf[obj.bucket.name.size() + 16];
  sprintf(buf, "%.4d-%.2d-%.2d-%lld-%s", (bdt.tm_year+1900), (bdt.tm_mon+1), bdt.tm_mday,
	  (long long)s->bucket.bucket_id, obj.bucket.name.c_str());
  string oid(buf);
  rgw_obj log_obj(intent_log_bucket, oid);

  bufferlist bl;
  ::encode(entry, bl);

  int ret = rgwstore->append_async(log_obj, bl.length(), bl);
  if (ret == -ENOENT) {
    string id;
    map<std::string, bufferlist> attrs;
    ret = rgwstore->create_bucket(id, intent_log_bucket, attrs, true);
    if (ret < 0)
      goto done;
    ret = rgwstore->append_async(log_obj, bl.length(), bl);
  }

done:
  return ret;
}
<commit_msg>rgw: don't log entries with bad utf8<commit_after>#include "common/Clock.h"
#include "common/utf8.h"

#include "rgw_log.h"
#include "rgw_acl.h"
#include "rgw_access.h"

#define DOUT_SUBSYS rgw

static rgw_bucket log_bucket(RGW_LOG_POOL_NAME);

static void set_param_str(struct req_state *s, const char *name, string& str)
{
  const char *p = s->env->get(name);
  if (p)
    str = p;
}

string render_log_object_name(const string& format,
			      struct tm *dt, int64_t bucket_id, const string& bucket_name)
{
  string o;
  for (unsigned i=0; i<format.size(); i++) {
    if (format[i] == '%' && i+1 < format.size()) {
      i++;
      char buf[32];
      switch (format[i]) {
      case '%':
	strcpy(buf, "%");
	break;
      case 'Y':
	sprintf(buf, "%.4d", dt->tm_year + 1900);
	break;
      case 'y':
	sprintf(buf, "%.2d", dt->tm_year % 100);
	break;
      case 'm':
	sprintf(buf, "%.2d", dt->tm_mon + 1);
	break;
      case 'd':
	sprintf(buf, "%.2d", dt->tm_mday);
	break;
      case 'H':
	sprintf(buf, "%.2d", dt->tm_hour);
	break;
      case 'I':
	sprintf(buf, "%.2d", (dt->tm_hour % 12) + 1);
	break;
      case 'k':
	sprintf(buf, "%d", dt->tm_hour);
	break;
      case 'l':
	sprintf(buf, "%d", (dt->tm_hour % 12) + 1);
	break;
      case 'M':
	sprintf(buf, "%.2d", dt->tm_min);
	break;

      case 'i':
	sprintf(buf, "%lld", (long long)bucket_id);
	break;
      case 'n':
	o += bucket_name;
	continue;
      default:
	// unknown code
	sprintf(buf, "%%%c", format[i]);
	break;
      }
      o += buf;
      continue;
    }
    o += format[i];
  }
  return o;
}

int rgw_log_op(struct req_state *s)
{
  struct rgw_log_entry entry;
  uint64_t bucket_id;

  if (!s->should_log)
    return 0;

  if (!s->bucket_name) {
    dout(0) << "nothing to log for operation" << dendl;
    return -EINVAL;
  }
  if (s->err.ret == -ERR_NO_SUCH_BUCKET) {
    if (!g_conf->rgw_log_nonexistent_bucket) {
      dout(0) << "bucket " << s->bucket << " doesn't exist, not logging" << dendl;
      return 0;
    }
    bucket_id = 0;
  } else {
    bucket_id = s->bucket.bucket_id;
  }
  entry.bucket = s->bucket_name;

  if (check_utf8(s->bucket_name, entry.bucket.size()) != 0) {
    dout(0) << "not logging op on bucket with non-utf8 name" << dendl;
    return 0;
  }

  if (s->object)
    entry.obj = s->object;
  else
    entry.obj = "-";

  entry.obj_size = s->obj_size;

  if (g_conf->rgw_remote_addr_param.length())
    set_param_str(s, g_conf->rgw_remote_addr_param.c_str(), entry.remote_addr);
  else
    set_param_str(s, "REMOTE_ADDR", entry.remote_addr);
  set_param_str(s, "HTTP_USER_AGENT", entry.user_agent);
  set_param_str(s, "HTTP_REFERRER", entry.referrer);
  set_param_str(s, "REQUEST_URI", entry.uri);
  set_param_str(s, "REQUEST_METHOD", entry.op);

  entry.user = s->user.user_id;
  if (s->acl)
    entry.object_owner = s->acl->get_owner().get_id();
  entry.bucket_owner = s->bucket_owner;

  entry.time = s->time;
  entry.total_time = ceph_clock_now(g_ceph_context) - s->time;
  entry.bytes_sent = s->bytes_sent;
  entry.bytes_received = s->bytes_received;
  if (s->err.http_ret) {
    char buf[16];
    snprintf(buf, sizeof(buf), "%d", s->err.http_ret);
    entry.http_status = buf;
  } else
    entry.http_status = "200"; // default

  entry.error_code = s->err.s3_code;
  entry.bucket_id = bucket_id;

  bufferlist bl;
  ::encode(entry, bl);

  struct tm bdt;
  time_t t = entry.time.sec();
  if (g_conf->rgw_log_object_name_utc)
    gmtime_r(&t, &bdt);
  else
    localtime_r(&t, &bdt);

  string oid = render_log_object_name(g_conf->rgw_log_object_name, &bdt,
				      s->bucket.bucket_id, entry.bucket.c_str());

  rgw_obj obj(log_bucket, oid);

  int ret = rgwstore->append_async(obj, bl.length(), bl);
  if (ret == -ENOENT) {
    string id;
    map<std::string, bufferlist> attrs;
    ret = rgwstore->create_bucket(id, log_bucket, attrs, true);
    if (ret < 0)
      goto done;
    // retry
    ret = rgwstore->append_async(obj, bl.length(), bl);
  }
done:
  if (ret < 0)
    dout(0) << "failed to log entry" << dendl;

  return ret;
}

int rgw_log_intent(struct req_state *s, rgw_obj& obj, RGWIntentEvent intent)
{
  rgw_bucket intent_log_bucket(RGW_INTENT_LOG_POOL_NAME);

  rgw_intent_log_entry entry;
  entry.obj = obj;
  entry.intent = (uint32_t)intent;
  entry.op_time = s->time;

  struct tm bdt;
  time_t t = entry.op_time.sec();
  if (g_conf->rgw_intent_log_object_name_utc)
    gmtime_r(&t, &bdt);
  else
    localtime_r(&t, &bdt);

  char buf[obj.bucket.name.size() + 16];
  sprintf(buf, "%.4d-%.2d-%.2d-%lld-%s", (bdt.tm_year+1900), (bdt.tm_mon+1), bdt.tm_mday,
	  (long long)s->bucket.bucket_id, obj.bucket.name.c_str());
  string oid(buf);
  rgw_obj log_obj(intent_log_bucket, oid);

  bufferlist bl;
  ::encode(entry, bl);

  int ret = rgwstore->append_async(log_obj, bl.length(), bl);
  if (ret == -ENOENT) {
    string id;
    map<std::string, bufferlist> attrs;
    ret = rgwstore->create_bucket(id, intent_log_bucket, attrs, true);
    if (ret < 0)
      goto done;
    ret = rgwstore->append_async(log_obj, bl.length(), bl);
  }

done:
  return ret;
}
<|endoftext|>


<commit_before>/*
 * Copyright 2007-2021 CM4all GmbH
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the
 * distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "Filter.hxx"
#include "CompletionHandler.hxx"
#include "lib/openssl/Name.hxx"
#include "lib/openssl/UniqueX509.hxx"
#include "FifoBufferBio.hxx"
#include "fs/ThreadSocketFilter.hxx"
#include "memory/fb_pool.hxx"
#include "memory/SliceFifoBuffer.hxx"
#include "util/AllocatedArray.hxx"
#include "util/AllocatedString.hxx"

#include <openssl/ssl.h>
#include <openssl/err.h>

#include <assert.h>
#include <string.h>

class SslFilter final : public ThreadSocketFilterHandler,
			SslCompletionHandler {
	/**
	 * Buffers which can be accessed from within the thread without
	 * holding locks.  These will be copied to/from the according
	 * #thread_socket_filter buffers.
	 */
	SliceFifoBuffer encrypted_input, decrypted_input,
		plain_output, encrypted_output;

	const UniqueSSL ssl;

	bool handshaking = true;

	AllocatedArray<unsigned char> alpn_selected;

public:
	AllocatedString peer_subject, peer_issuer_subject;

	SslFilter(UniqueSSL &&_ssl)
		:ssl(std::move(_ssl)) {
		SSL_set_bio(ssl.get(),
			    NewFifoBufferBio(encrypted_input),
			    NewFifoBufferBio(encrypted_output));

		SetSslCompletionHandler(*ssl, *this);
	}

	std::span<const unsigned char> GetAlpnSelected() const noexcept {
		return {alpn_selected.data(), alpn_selected.size()};
	}

private:
	/**
	 * Called from inside Run() right after the handshake has
	 * completed.  This is used to collect some data for our
	 * public getters.
	 */
	void PostHandshake() noexcept;

	void Encrypt();

	/* virtual methods from class ThreadSocketFilterHandler */
	void PreRun(ThreadSocketFilterInternal &f) noexcept override;
	void Run(ThreadSocketFilterInternal &f) override;
	void PostRun(ThreadSocketFilterInternal &f) noexcept override;
	void CancelRun(ThreadSocketFilterInternal &f) noexcept override;

	/* virtual methods from class SslCompletionHandler */
	void OnSslCompletion() noexcept override {
		ScheduleRun();
	}
};

static std::runtime_error
MakeSslError()
{
	unsigned long error = ERR_get_error();
	char buffer[120];
	return std::runtime_error(ERR_error_string(error, buffer));
}

static AllocatedString
format_subject_name(X509 *cert)
{
	return ToString(X509_get_subject_name(cert));
}

static AllocatedString
format_issuer_subject_name(X509 *cert)
{
	return ToString(X509_get_issuer_name(cert));
}

[[gnu::pure]]
static bool
is_ssl_error(SSL *ssl, int ret)
{
	if (ret == 0)
		/* this is always an error according to the documentation of
		   SSL_read(), SSL_write() and SSL_do_handshake() */
		return true;

	switch (SSL_get_error(ssl, ret)) {
	case SSL_ERROR_NONE:
	case SSL_ERROR_WANT_READ:
	case SSL_ERROR_WANT_WRITE:
	case SSL_ERROR_WANT_CONNECT:
	case SSL_ERROR_WANT_ACCEPT:
	case SSL_ERROR_WANT_X509_LOOKUP:
		return false;

	default:
		return true;
	}
}

static void
CheckThrowSslError(SSL *ssl, int result)
{
	if (is_ssl_error(ssl, result))
		throw MakeSslError();
}

inline void
SslFilter::PostHandshake() noexcept
{
	const unsigned char *alpn_data;
	unsigned int alpn_length;
	SSL_get0_alpn_selected(ssl.get(), &alpn_data, &alpn_length);
	if (alpn_length > 0)
		alpn_selected = ConstBuffer<unsigned char>(alpn_data,
							   alpn_length);

	UniqueX509 cert(SSL_get_peer_certificate(ssl.get()));
	if (cert != nullptr) {
		peer_subject = format_subject_name(cert.get());
		peer_issuer_subject = format_issuer_subject_name(cert.get());
	}
}

enum class SslDecryptResult {
	SUCCESS,

	/**
	 * More encrypted_input data is required.
	 */
	MORE,

	CLOSE_NOTIFY_ALERT,
};

static SslDecryptResult
ssl_decrypt(SSL *ssl, ForeignFifoBuffer<std::byte> &buffer)
{
	/* SSL_read() must be called repeatedly until there is no more
	   data (or until the buffer is full) */

	while (true) {
		auto w = buffer.Write();
		if (w.empty())
			return SslDecryptResult::SUCCESS;

		int result = SSL_read(ssl, w.data(), w.size());
		if (result < 0 && SSL_get_error(ssl, result) == SSL_ERROR_WANT_READ)
			return SslDecryptResult::MORE;

		if (result <= 0) {
			if (SSL_get_error(ssl, result) == SSL_ERROR_ZERO_RETURN)
				/* got a "close notify" alert from the peer */
				return SslDecryptResult::CLOSE_NOTIFY_ALERT;

			CheckThrowSslError(ssl, result);
			return SslDecryptResult::SUCCESS;
		}

		buffer.Append(result);
	}
}

static void
ssl_encrypt(SSL *ssl, ForeignFifoBuffer<std::byte> &buffer)
{
	/* SSL_write() must be called repeatedly until there is no more
	   data; with SSL_MODE_ENABLE_PARTIAL_WRITE, SSL_write() finishes
	   only the current incomplete record, and additional data which
	   has been submitted more recently will only be considered in the
	   next SSL_write() call */

	while (true) {
		auto r = buffer.Read();
		if (r.empty())
			return;

		int result = SSL_write(ssl, r.data(), r.size());
		if (result <= 0) {
			CheckThrowSslError(ssl, result);
			return;
		}

		buffer.Consume(result);
	}
}

inline void
SslFilter::Encrypt()
{
	ssl_encrypt(ssl.get(), plain_output);
}

/*
 * thread_socket_filter_handler
 *
 */

void
SslFilter::PreRun(ThreadSocketFilterInternal &f) noexcept
{
	if (f.IsIdle()) {
		decrypted_input.AllocateIfNull(fb_pool_get());
		encrypted_output.AllocateIfNull(fb_pool_get());
	}
}

void
SslFilter::Run(ThreadSocketFilterInternal &f)
{
	/* copy input (and output to make room for more output) */

	{
		std::unique_lock<std::mutex> lock(f.mutex);

		if (f.decrypted_input.IsNull() || f.encrypted_output.IsNull()) {
			/* retry, let PreRun() allocate the missing buffer */
			f.again = true;
			return;
		}

		f.decrypted_input.MoveFromAllowNull(decrypted_input);

		plain_output.MoveFromAllowNull(f.plain_output);
		encrypted_input.MoveFromAllowSrcNull(f.encrypted_input);

		f.encrypted_output.MoveFromAllowNull(encrypted_output);

		if (decrypted_input.IsNull() || encrypted_output.IsNull()) {
			/* retry, let PreRun() allocate the missing buffer */
			f.again = true;
			return;
		}
	}

	/* let OpenSSL work */

	ERR_clear_error();

	if (handshaking) [[unlikely]] {
		int result = SSL_do_handshake(ssl.get());
		if (result == 1) {
			handshaking = false;
			PostHandshake();
		} else {
			try {
				CheckThrowSslError(ssl.get(), result);
				/* flush the encrypted_output buffer, because it may
				   contain a "TLS alert" */
			} catch (...) {
				const std::lock_guard<std::mutex> lock(f.mutex);
				f.encrypted_output.MoveFromAllowNull(encrypted_output);
				throw;
			}
		}
	}

	if (!handshaking) [[likely]] {
		Encrypt();

		switch (ssl_decrypt(ssl.get(), decrypted_input)) {
		case SslDecryptResult::SUCCESS:
			break;

		case SslDecryptResult::MORE:
			if (encrypted_input.IsDefinedAndFull())
				throw std::runtime_error("SSL encrypted_input buffer is full");

			break;

		case SslDecryptResult::CLOSE_NOTIFY_ALERT:
			{
				std::unique_lock<std::mutex> lock(f.mutex);
				f.input_eof = true;
			}
			break;
		}
	}

	/* copy output */

	{
		std::unique_lock<std::mutex> lock(f.mutex);

		f.decrypted_input.MoveFromAllowNull(decrypted_input);
		f.encrypted_output.MoveFromAllowNull(encrypted_output);
		f.drained = plain_output.empty() && encrypted_output.empty();

		if (!decrypted_input.IsDefinedAndFull() && !f.encrypted_input.empty())
			/* there's more data to be decrypted and we
			   still have room in the destination buffer,
			   so let's run again */
			f.again = true;

		if (!f.plain_output.empty() && !plain_output.IsDefinedAndFull() &&
		    !encrypted_output.IsDefinedAndFull())
			/* there's more data, and we're ready to handle it: try
			   again */
			f.again = true;

		f.handshaking = handshaking;
	}
}

void
SslFilter::PostRun(ThreadSocketFilterInternal &f) noexcept
{
	if (f.IsIdle()) {
		plain_output.FreeIfEmpty();
		encrypted_input.FreeIfEmpty();
		decrypted_input.FreeIfEmpty();
		encrypted_output.FreeIfEmpty();
	}
}

void
SslFilter::CancelRun(ThreadSocketFilterInternal &) noexcept
{
	if (cancel_ptr)
		/* cancel the CertCache::Apply() call */
		cancel_ptr.Cancel();
}

/*
 * constructor
 *
 */

std::unique_ptr<ThreadSocketFilterHandler>
ssl_filter_new(UniqueSSL &&ssl) noexcept
{
	return std::make_unique<SslFilter>(std::move(ssl));
}

SslFilter &
ssl_filter_cast_from(ThreadSocketFilterHandler &tsfh) noexcept
{
	return static_cast<SslFilter &>(tsfh);
}

const SslFilter *
ssl_filter_cast_from(const SocketFilter *socket_filter) noexcept
{
	const auto *tsf = dynamic_cast<const ThreadSocketFilter *>(socket_filter);
	if (tsf == nullptr)
		return nullptr;

	return dynamic_cast<const SslFilter *>(&tsf->GetHandler());
}

std::span<const unsigned char>
ssl_filter_get_alpn_selected(const SslFilter &ssl) noexcept
{
	return ssl.GetAlpnSelected();
}

const char *
ssl_filter_get_peer_subject(const SslFilter &ssl) noexcept
{
	return ssl.peer_subject.c_str();
}

const char *
ssl_filter_get_peer_issuer_subject(const SslFilter &ssl) noexcept
{
	return ssl.peer_issuer_subject.c_str();
}
<commit_msg>ssl/Filter: use std::span<commit_after>/*
 * Copyright 2007-2021 CM4all GmbH
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the
 * distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "Filter.hxx"
#include "CompletionHandler.hxx"
#include "lib/openssl/Name.hxx"
#include "lib/openssl/UniqueX509.hxx"
#include "FifoBufferBio.hxx"
#include "fs/ThreadSocketFilter.hxx"
#include "memory/fb_pool.hxx"
#include "memory/SliceFifoBuffer.hxx"
#include "util/AllocatedArray.hxx"
#include "util/AllocatedString.hxx"

#include <openssl/ssl.h>
#include <openssl/err.h>

#include <assert.h>
#include <string.h>

class SslFilter final : public ThreadSocketFilterHandler,
			SslCompletionHandler {
	/**
	 * Buffers which can be accessed from within the thread without
	 * holding locks.  These will be copied to/from the according
	 * #thread_socket_filter buffers.
	 */
	SliceFifoBuffer encrypted_input, decrypted_input,
		plain_output, encrypted_output;

	const UniqueSSL ssl;

	bool handshaking = true;

	AllocatedArray<unsigned char> alpn_selected;

public:
	AllocatedString peer_subject, peer_issuer_subject;

	SslFilter(UniqueSSL &&_ssl)
		:ssl(std::move(_ssl)) {
		SSL_set_bio(ssl.get(),
			    NewFifoBufferBio(encrypted_input),
			    NewFifoBufferBio(encrypted_output));

		SetSslCompletionHandler(*ssl, *this);
	}

	std::span<const unsigned char> GetAlpnSelected() const noexcept {
		return {alpn_selected.data(), alpn_selected.size()};
	}

private:
	/**
	 * Called from inside Run() right after the handshake has
	 * completed.  This is used to collect some data for our
	 * public getters.
	 */
	void PostHandshake() noexcept;

	void Encrypt();

	/* virtual methods from class ThreadSocketFilterHandler */
	void PreRun(ThreadSocketFilterInternal &f) noexcept override;
	void Run(ThreadSocketFilterInternal &f) override;
	void PostRun(ThreadSocketFilterInternal &f) noexcept override;
	void CancelRun(ThreadSocketFilterInternal &f) noexcept override;

	/* virtual methods from class SslCompletionHandler */
	void OnSslCompletion() noexcept override {
		ScheduleRun();
	}
};

static std::runtime_error
MakeSslError()
{
	unsigned long error = ERR_get_error();
	char buffer[120];
	return std::runtime_error(ERR_error_string(error, buffer));
}

static AllocatedString
format_subject_name(X509 *cert)
{
	return ToString(X509_get_subject_name(cert));
}

static AllocatedString
format_issuer_subject_name(X509 *cert)
{
	return ToString(X509_get_issuer_name(cert));
}

[[gnu::pure]]
static bool
is_ssl_error(SSL *ssl, int ret)
{
	if (ret == 0)
		/* this is always an error according to the documentation of
		   SSL_read(), SSL_write() and SSL_do_handshake() */
		return true;

	switch (SSL_get_error(ssl, ret)) {
	case SSL_ERROR_NONE:
	case SSL_ERROR_WANT_READ:
	case SSL_ERROR_WANT_WRITE:
	case SSL_ERROR_WANT_CONNECT:
	case SSL_ERROR_WANT_ACCEPT:
	case SSL_ERROR_WANT_X509_LOOKUP:
		return false;

	default:
		return true;
	}
}

static void
CheckThrowSslError(SSL *ssl, int result)
{
	if (is_ssl_error(ssl, result))
		throw MakeSslError();
}

inline void
SslFilter::PostHandshake() noexcept
{
	const unsigned char *alpn_data;
	unsigned int alpn_length;
	SSL_get0_alpn_selected(ssl.get(), &alpn_data, &alpn_length);
	if (alpn_length > 0)
		alpn_selected = std::span<const unsigned char>(alpn_data,
							       alpn_length);

	UniqueX509 cert(SSL_get_peer_certificate(ssl.get()));
	if (cert != nullptr) {
		peer_subject = format_subject_name(cert.get());
		peer_issuer_subject = format_issuer_subject_name(cert.get());
	}
}

enum class SslDecryptResult {
	SUCCESS,

	/**
	 * More encrypted_input data is required.
	 */
	MORE,

	CLOSE_NOTIFY_ALERT,
};

static SslDecryptResult
ssl_decrypt(SSL *ssl, ForeignFifoBuffer<std::byte> &buffer)
{
	/* SSL_read() must be called repeatedly until there is no more
	   data (or until the buffer is full) */

	while (true) {
		auto w = buffer.Write();
		if (w.empty())
			return SslDecryptResult::SUCCESS;

		int result = SSL_read(ssl, w.data(), w.size());
		if (result < 0 && SSL_get_error(ssl, result) == SSL_ERROR_WANT_READ)
			return SslDecryptResult::MORE;

		if (result <= 0) {
			if (SSL_get_error(ssl, result) == SSL_ERROR_ZERO_RETURN)
				/* got a "close notify" alert from the peer */
				return SslDecryptResult::CLOSE_NOTIFY_ALERT;

			CheckThrowSslError(ssl, result);
			return SslDecryptResult::SUCCESS;
		}

		buffer.Append(result);
	}
}

static void
ssl_encrypt(SSL *ssl, ForeignFifoBuffer<std::byte> &buffer)
{
	/* SSL_write() must be called repeatedly until there is no more
	   data; with SSL_MODE_ENABLE_PARTIAL_WRITE, SSL_write() finishes
	   only the current incomplete record, and additional data which
	   has been submitted more recently will only be considered in the
	   next SSL_write() call */

	while (true) {
		auto r = buffer.Read();
		if (r.empty())
			return;

		int result = SSL_write(ssl, r.data(), r.size());
		if (result <= 0) {
			CheckThrowSslError(ssl, result);
			return;
		}

		buffer.Consume(result);
	}
}

inline void
SslFilter::Encrypt()
{
	ssl_encrypt(ssl.get(), plain_output);
}

/*
 * thread_socket_filter_handler
 *
 */

void
SslFilter::PreRun(ThreadSocketFilterInternal &f) noexcept
{
	if (f.IsIdle()) {
		decrypted_input.AllocateIfNull(fb_pool_get());
		encrypted_output.AllocateIfNull(fb_pool_get());
	}
}

void
SslFilter::Run(ThreadSocketFilterInternal &f)
{
	/* copy input (and output to make room for more output) */

	{
		std::unique_lock<std::mutex> lock(f.mutex);

		if (f.decrypted_input.IsNull() || f.encrypted_output.IsNull()) {
			/* retry, let PreRun() allocate the missing buffer */
			f.again = true;
			return;
		}

		f.decrypted_input.MoveFromAllowNull(decrypted_input);

		plain_output.MoveFromAllowNull(f.plain_output);
		encrypted_input.MoveFromAllowSrcNull(f.encrypted_input);

		f.encrypted_output.MoveFromAllowNull(encrypted_output);

		if (decrypted_input.IsNull() || encrypted_output.IsNull()) {
			/* retry, let PreRun() allocate the missing buffer */
			f.again = true;
			return;
		}
	}

	/* let OpenSSL work */

	ERR_clear_error();

	if (handshaking) [[unlikely]] {
		int result = SSL_do_handshake(ssl.get());
		if (result == 1) {
			handshaking = false;
			PostHandshake();
		} else {
			try {
				CheckThrowSslError(ssl.get(), result);
				/* flush the encrypted_output buffer, because it may
				   contain a "TLS alert" */
			} catch (...) {
				const std::lock_guard<std::mutex> lock(f.mutex);
				f.encrypted_output.MoveFromAllowNull(encrypted_output);
				throw;
			}
		}
	}

	if (!handshaking) [[likely]] {
		Encrypt();

		switch (ssl_decrypt(ssl.get(), decrypted_input)) {
		case SslDecryptResult::SUCCESS:
			break;

		case SslDecryptResult::MORE:
			if (encrypted_input.IsDefinedAndFull())
				throw std::runtime_error("SSL encrypted_input buffer is full");

			break;

		case SslDecryptResult::CLOSE_NOTIFY_ALERT:
			{
				std::unique_lock<std::mutex> lock(f.mutex);
				f.input_eof = true;
			}
			break;
		}
	}

	/* copy output */

	{
		std::unique_lock<std::mutex> lock(f.mutex);

		f.decrypted_input.MoveFromAllowNull(decrypted_input);
		f.encrypted_output.MoveFromAllowNull(encrypted_output);
		f.drained = plain_output.empty() && encrypted_output.empty();

		if (!decrypted_input.IsDefinedAndFull() && !f.encrypted_input.empty())
			/* there's more data to be decrypted and we
			   still have room in the destination buffer,
			   so let's run again */
			f.again = true;

		if (!f.plain_output.empty() && !plain_output.IsDefinedAndFull() &&
		    !encrypted_output.IsDefinedAndFull())
			/* there's more data, and we're ready to handle it: try
			   again */
			f.again = true;

		f.handshaking = handshaking;
	}
}

void
SslFilter::PostRun(ThreadSocketFilterInternal &f) noexcept
{
	if (f.IsIdle()) {
		plain_output.FreeIfEmpty();
		encrypted_input.FreeIfEmpty();
		decrypted_input.FreeIfEmpty();
		encrypted_output.FreeIfEmpty();
	}
}

void
SslFilter::CancelRun(ThreadSocketFilterInternal &) noexcept
{
	if (cancel_ptr)
		/* cancel the CertCache::Apply() call */
		cancel_ptr.Cancel();
}

/*
 * constructor
 *
 */

std::unique_ptr<ThreadSocketFilterHandler>
ssl_filter_new(UniqueSSL &&ssl) noexcept
{
	return std::make_unique<SslFilter>(std::move(ssl));
}

SslFilter &
ssl_filter_cast_from(ThreadSocketFilterHandler &tsfh) noexcept
{
	return static_cast<SslFilter &>(tsfh);
}

const SslFilter *
ssl_filter_cast_from(const SocketFilter *socket_filter) noexcept
{
	const auto *tsf = dynamic_cast<const ThreadSocketFilter *>(socket_filter);
	if (tsf == nullptr)
		return nullptr;

	return dynamic_cast<const SslFilter *>(&tsf->GetHandler());
}

std::span<const unsigned char>
ssl_filter_get_alpn_selected(const SslFilter &ssl) noexcept
{
	return ssl.GetAlpnSelected();
}

const char *
ssl_filter_get_peer_subject(const SslFilter &ssl) noexcept
{
	return ssl.peer_subject.c_str();
}

const char *
ssl_filter_get_peer_issuer_subject(const SslFilter &ssl) noexcept
{
	return ssl.peer_issuer_subject.c_str();
}
<|endoftext|>


<commit_before>/*
 * bacteria-core, core for cellular automaton
 * Copyright (C) 2016 Pavel Dolgov
 *
 * See the LICENSE file for terms of use.
 */

#include <boost/foreach.hpp>
#include <boost/test/unit_test.hpp>

#include "Model.hpp"

typedef void (Implementation::Model::*OneArgMethod) (
    const Abstract::Point& coordinates
);

typedef void (Implementation::Model::*TwoArgsMethod) (
    const Abstract::Point& coordinates,
    int change
);

typedef void (Implementation::Model::*MultiArgsMethod) (
    const Abstract::Point& coordinates,
    int mass,
    int direction,
    int team,
    int instruction
);

template<typename Func>
void checkModelMethodForThrow(
    Implementation::Model* model,
    Func model_method,
    int arg1,
    int arg2
) {
    BOOST_REQUIRE_THROW(
        ((*model).*model_method)(arg1, arg2), Exception
    );
}

template<>
void checkModelMethodForThrow<OneArgMethod>(
    Implementation::Model* model,
    OneArgMethod model_method,
    int arg1,
    int arg2
) {
    Abstract::Point coordinates(arg1, arg2);
    BOOST_REQUIRE_THROW(
        ((*model).*model_method)(coordinates), Exception
    );
}

template<>
void checkModelMethodForThrow<TwoArgsMethod>(
    Implementation::Model* model,
    TwoArgsMethod model_method,
    int arg1,
    int arg2
) {
    Abstract::Point coordinates(arg1, arg2);
    BOOST_REQUIRE_THROW(
        ((*model).*model_method)(coordinates, 0), Exception
    );
}

template<>
void checkModelMethodForThrow<MultiArgsMethod>(
    Implementation::Model* model,
    MultiArgsMethod model_method,
    int arg1,
    int arg2
) {
    Abstract::Point coordinates(arg1, arg2);
    BOOST_REQUIRE_THROW(
        ((*model).*model_method)(
            coordinates,
            DEFAULT_MASS,
            0,
            0,
            0
        ),
        Exception
    );
}

// "dead" test: attempt to do something with dead bacterium
template<typename Func>
static void deadTest(
    Implementation::Model* model,
    Func model_method
) {
    model->kill(0, 0);
    checkModelMethodForThrow(
        model,
        model_method,
        0,
        0
    );
}

template<typename Func>
static void checkErrorHandling(
    Implementation::Model* model,
    Func model_method,
    bool dead_test
) {
    // Range errors: test all combinations of
    // "wrong" (outside of correct range) arguments.
    // This solution works for coordinates and non-coordinates
    // methods of Model. (< 0, = 0, > MAX)
    int wrong_args[] = {-1, 0, MIN_WIDTH};
    BOOST_FOREACH (int arg1, wrong_args) {
        BOOST_FOREACH (int arg2, wrong_args) {
            if ((arg1 != 0) || (arg2 != 0)) {
                // (0, 0) is correct
                checkModelMethodForThrow(
                    model,
                    model_method,
                    arg1,
                    arg2
                );
            }
        }
    }
    if (dead_test) {
        // "dead" error
        // (attempt to do something with dead bacterium)
        deadTest(model, model_method);
    }
}

static Abstract::Point createInBaseCoordinates(
    Implementation::Model* model
) {
    Abstract::Point coordinates(0, 0);
    model->createNewByCoordinates(
        coordinates,
        DEFAULT_MASS,
        0,
        0,
        0
    );
    return coordinates;
}

static Implementation::Model* createBaseModel(
    int bacteria = 0,
    int teams = 1
) {
    Implementation::Model* model =
        Abstract::makeModel<Implementation::Model>(
            MIN_WIDTH,
            MIN_HEIGHT,
            bacteria,
            teams
        );
    return model;
}

BOOST_AUTO_TEST_CASE (bacteria_number_test) {
    Implementation::Model* model = createBaseModel();
    int bacteria_number = model->getBacteriaNumber(0);
    BOOST_REQUIRE(bacteria_number == 0);
    createInBaseCoordinates(model);
    bacteria_number = model->getBacteriaNumber(0);
    BOOST_REQUIRE(bacteria_number == 1);
    // range errors
    BOOST_REQUIRE_THROW(model->getBacteriaNumber(-1), Exception);
    BOOST_REQUIRE_THROW(model->getBacteriaNumber(1), Exception);
    delete model;
}

BOOST_AUTO_TEST_CASE (get_instruction_test) {
    Implementation::Model* model = createBaseModel(1, 1);
    int instruction = model->getInstruction(0, 0);
    BOOST_REQUIRE(instruction == 0);
    checkErrorHandling(
        model,
        &Implementation::Model::getInstruction,
        true
    );
    delete model;
}

BOOST_AUTO_TEST_CASE (get_coordinates_test) {
    Implementation::Model* model = createBaseModel();
    Abstract::Point coordinates = createInBaseCoordinates(model);
    Abstract::Point derived_coordinates = model->getCoordinates(0, 0);
    BOOST_REQUIRE(derived_coordinates == coordinates);
    checkErrorHandling(
        model,
        &Implementation::Model::getCoordinates,
        true
    );
    delete model;
}

BOOST_AUTO_TEST_CASE (get_direction_test) {
    Implementation::Model* model = createBaseModel();
    createInBaseCoordinates(model);
    int direction = model->getDirection(0, 0);
    BOOST_REQUIRE(direction == Abstract::LEFT);
    checkErrorHandling(
        model,
        &Implementation::Model::getDirection,
        true
    );
    delete model;
}

BOOST_AUTO_TEST_CASE (get_mass_test) {
    Implementation::Model* model = createBaseModel(1, 1);
    int mass = model->getMass(0, 0);
    BOOST_REQUIRE(mass == DEFAULT_MASS);
    checkErrorHandling(
        model,
        &Implementation::Model::getMass,
        true
    );
    delete model;
}

BOOST_AUTO_TEST_CASE (height_test) {
    Implementation::Model* model = createBaseModel();
    BOOST_REQUIRE(model->getHeight() == MIN_HEIGHT);
    delete model;
}

BOOST_AUTO_TEST_CASE (width_test) {
    Implementation::Model* model = createBaseModel();
    BOOST_REQUIRE(model->getWidth() == MIN_WIDTH);
    delete model;
}

BOOST_AUTO_TEST_CASE (kill_test) {
    Implementation::Model* model = createBaseModel();
    Abstract::Point coordinates = createInBaseCoordinates(model);
    model->kill(0, 0);
    Abstract::CellState state = model->cellState(coordinates);
    BOOST_REQUIRE(state == Abstract::EMPTY);
    // error handling checks
    createInBaseCoordinates(model);
    // FIXME test doesn't work correctly without this function call.
    // The solution is to use set instead of vector in model.
    model->clearBeforeMove(0);
    checkErrorHandling(
        model,
        &Implementation::Model::kill,
        true
    );
    delete model;
}

BOOST_AUTO_TEST_CASE (create_coordinates_test) {
    Implementation::Model* model = createBaseModel();
    Abstract::Point coordinates = createInBaseCoordinates(model);
    Abstract::CellState state = model->cellState(coordinates);
    BOOST_REQUIRE(state == Abstract::BACTERIUM);
    checkErrorHandling<MultiArgsMethod>(
        model,
        &Implementation::Model::createNewByCoordinates,
        false
    );
    delete model;
}

BOOST_AUTO_TEST_CASE (change_mass_coordinates_test) {
    Implementation::Model* model = createBaseModel();
    Abstract::Point coordinates = createInBaseCoordinates(model);
    int test_val = 1;
    model->changeMassByCoordinates(coordinates, test_val);
    int new_mass = model->getMassByCoordinates(coordinates);
    BOOST_REQUIRE(new_mass == (DEFAULT_MASS + test_val));
    model->changeMassByCoordinates(coordinates, -test_val);
    new_mass = model->getMassByCoordinates(coordinates);
    BOOST_REQUIRE(new_mass == DEFAULT_MASS);
    checkErrorHandling<TwoArgsMethod>(
        model,
        &Implementation::Model::changeMassByCoordinates,
        true
    );
    delete model;
}

BOOST_AUTO_TEST_CASE (kill_coordinates_test) {
    Implementation::Model* model = createBaseModel();
    Abstract::Point coordinates = createInBaseCoordinates(model);
    model->killByCoordinates(coordinates);
    Abstract::CellState state = model->cellState(coordinates);
    BOOST_REQUIRE(state == Abstract::EMPTY);
    checkErrorHandling<OneArgMethod>(
        model,
        &Implementation::Model::killByCoordinates,
        false
    );
    delete model;
}
<commit_msg>Model tests: add isAlive() check to kill_test<commit_after>/*
 * bacteria-core, core for cellular automaton
 * Copyright (C) 2016 Pavel Dolgov
 *
 * See the LICENSE file for terms of use.
 */

#include <boost/foreach.hpp>
#include <boost/test/unit_test.hpp>

#include "Model.hpp"

typedef void (Implementation::Model::*OneArgMethod) (
    const Abstract::Point& coordinates
);

typedef void (Implementation::Model::*TwoArgsMethod) (
    const Abstract::Point& coordinates,
    int change
);

typedef void (Implementation::Model::*MultiArgsMethod) (
    const Abstract::Point& coordinates,
    int mass,
    int direction,
    int team,
    int instruction
);

template<typename Func>
void checkModelMethodForThrow(
    Implementation::Model* model,
    Func model_method,
    int arg1,
    int arg2
) {
    BOOST_REQUIRE_THROW(
        ((*model).*model_method)(arg1, arg2), Exception
    );
}

template<>
void checkModelMethodForThrow<OneArgMethod>(
    Implementation::Model* model,
    OneArgMethod model_method,
    int arg1,
    int arg2
) {
    Abstract::Point coordinates(arg1, arg2);
    BOOST_REQUIRE_THROW(
        ((*model).*model_method)(coordinates), Exception
    );
}

template<>
void checkModelMethodForThrow<TwoArgsMethod>(
    Implementation::Model* model,
    TwoArgsMethod model_method,
    int arg1,
    int arg2
) {
    Abstract::Point coordinates(arg1, arg2);
    BOOST_REQUIRE_THROW(
        ((*model).*model_method)(coordinates, 0), Exception
    );
}

template<>
void checkModelMethodForThrow<MultiArgsMethod>(
    Implementation::Model* model,
    MultiArgsMethod model_method,
    int arg1,
    int arg2
) {
    Abstract::Point coordinates(arg1, arg2);
    BOOST_REQUIRE_THROW(
        ((*model).*model_method)(
            coordinates,
            DEFAULT_MASS,
            0,
            0,
            0
        ),
        Exception
    );
}

// "dead" test: attempt to do something with dead bacterium
template<typename Func>
static void deadTest(
    Implementation::Model* model,
    Func model_method
) {
    model->kill(0, 0);
    checkModelMethodForThrow(
        model,
        model_method,
        0,
        0
    );
}

template<typename Func>
static void checkErrorHandling(
    Implementation::Model* model,
    Func model_method,
    bool dead_test
) {
    // Range errors: test all combinations of
    // "wrong" (outside of correct range) arguments.
    // This solution works for coordinates and non-coordinates
    // methods of Model. (< 0, = 0, > MAX)
    int wrong_args[] = {-1, 0, MIN_WIDTH};
    BOOST_FOREACH (int arg1, wrong_args) {
        BOOST_FOREACH (int arg2, wrong_args) {
            if ((arg1 != 0) || (arg2 != 0)) {
                // (0, 0) is correct
                checkModelMethodForThrow(
                    model,
                    model_method,
                    arg1,
                    arg2
                );
            }
        }
    }
    if (dead_test) {
        // "dead" error
        // (attempt to do something with dead bacterium)
        deadTest(model, model_method);
    }
}

static Abstract::Point createInBaseCoordinates(
    Implementation::Model* model
) {
    Abstract::Point coordinates(0, 0);
    model->createNewByCoordinates(
        coordinates,
        DEFAULT_MASS,
        0,
        0,
        0
    );
    return coordinates;
}

static Implementation::Model* createBaseModel(
    int bacteria = 0,
    int teams = 1
) {
    Implementation::Model* model =
        Abstract::makeModel<Implementation::Model>(
            MIN_WIDTH,
            MIN_HEIGHT,
            bacteria,
            teams
        );
    return model;
}

BOOST_AUTO_TEST_CASE (bacteria_number_test) {
    Implementation::Model* model = createBaseModel();
    int bacteria_number = model->getBacteriaNumber(0);
    BOOST_REQUIRE(bacteria_number == 0);
    createInBaseCoordinates(model);
    bacteria_number = model->getBacteriaNumber(0);
    BOOST_REQUIRE(bacteria_number == 1);
    // range errors
    BOOST_REQUIRE_THROW(model->getBacteriaNumber(-1), Exception);
    BOOST_REQUIRE_THROW(model->getBacteriaNumber(1), Exception);
    delete model;
}

BOOST_AUTO_TEST_CASE (get_instruction_test) {
    Implementation::Model* model = createBaseModel(1, 1);
    int instruction = model->getInstruction(0, 0);
    BOOST_REQUIRE(instruction == 0);
    checkErrorHandling(
        model,
        &Implementation::Model::getInstruction,
        true
    );
    delete model;
}

BOOST_AUTO_TEST_CASE (get_coordinates_test) {
    Implementation::Model* model = createBaseModel();
    Abstract::Point coordinates = createInBaseCoordinates(model);
    Abstract::Point derived_coordinates = model->getCoordinates(0, 0);
    BOOST_REQUIRE(derived_coordinates == coordinates);
    checkErrorHandling(
        model,
        &Implementation::Model::getCoordinates,
        true
    );
    delete model;
}

BOOST_AUTO_TEST_CASE (get_direction_test) {
    Implementation::Model* model = createBaseModel();
    createInBaseCoordinates(model);
    int direction = model->getDirection(0, 0);
    BOOST_REQUIRE(direction == Abstract::LEFT);
    checkErrorHandling(
        model,
        &Implementation::Model::getDirection,
        true
    );
    delete model;
}

BOOST_AUTO_TEST_CASE (get_mass_test) {
    Implementation::Model* model = createBaseModel(1, 1);
    int mass = model->getMass(0, 0);
    BOOST_REQUIRE(mass == DEFAULT_MASS);
    checkErrorHandling(
        model,
        &Implementation::Model::getMass,
        true
    );
    delete model;
}

BOOST_AUTO_TEST_CASE (height_test) {
    Implementation::Model* model = createBaseModel();
    BOOST_REQUIRE(model->getHeight() == MIN_HEIGHT);
    delete model;
}

BOOST_AUTO_TEST_CASE (width_test) {
    Implementation::Model* model = createBaseModel();
    BOOST_REQUIRE(model->getWidth() == MIN_WIDTH);
    delete model;
}

BOOST_AUTO_TEST_CASE (kill_test) {
    Implementation::Model* model = createBaseModel();
    Abstract::Point coordinates = createInBaseCoordinates(model);
    model->kill(0, 0);
    Abstract::CellState state = model->cellState(coordinates);
    BOOST_REQUIRE(state == Abstract::EMPTY);
    BOOST_REQUIRE(model->isAlive(0, 0) == false);
    // error handling checks
    createInBaseCoordinates(model);
    // FIXME test doesn't work correctly without this function call.
    // The solution is to use set instead of vector in model.
    model->clearBeforeMove(0);
    checkErrorHandling(
        model,
        &Implementation::Model::kill,
        true
    );
    delete model;
}

BOOST_AUTO_TEST_CASE (create_coordinates_test) {
    Implementation::Model* model = createBaseModel();
    Abstract::Point coordinates = createInBaseCoordinates(model);
    Abstract::CellState state = model->cellState(coordinates);
    BOOST_REQUIRE(state == Abstract::BACTERIUM);
    checkErrorHandling<MultiArgsMethod>(
        model,
        &Implementation::Model::createNewByCoordinates,
        false
    );
    delete model;
}

BOOST_AUTO_TEST_CASE (change_mass_coordinates_test) {
    Implementation::Model* model = createBaseModel();
    Abstract::Point coordinates = createInBaseCoordinates(model);
    int test_val = 1;
    model->changeMassByCoordinates(coordinates, test_val);
    int new_mass = model->getMassByCoordinates(coordinates);
    BOOST_REQUIRE(new_mass == (DEFAULT_MASS + test_val));
    model->changeMassByCoordinates(coordinates, -test_val);
    new_mass = model->getMassByCoordinates(coordinates);
    BOOST_REQUIRE(new_mass == DEFAULT_MASS);
    checkErrorHandling<TwoArgsMethod>(
        model,
        &Implementation::Model::changeMassByCoordinates,
        true
    );
    delete model;
}

BOOST_AUTO_TEST_CASE (kill_coordinates_test) {
    Implementation::Model* model = createBaseModel();
    Abstract::Point coordinates = createInBaseCoordinates(model);
    model->killByCoordinates(coordinates);
    Abstract::CellState state = model->cellState(coordinates);
    BOOST_REQUIRE(state == Abstract::EMPTY);
    checkErrorHandling<OneArgMethod>(
        model,
        &Implementation::Model::killByCoordinates,
        false
    );
    delete model;
}
<|endoftext|>


<commit_before>/*
 *  This file is part of WinSparkle (http://winsparkle.org)
 *
 *  Copyright (C) 2009 Vaclav Slavik
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a
 *  copy of this software and associated documentation files (the "Software"),
 *  to deal in the Software without restriction, including without limitation
 *  the rights to use, copy, modify, merge, publish, distribute, sublicense,
 *  and/or sell copies of the Software, and to permit persons to whom the
 *  Software is furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 *  DEALINGS IN THE SOFTWARE.
 *
 */

#include "threads.h"
#include "error.h"

#include <windows.h>
#include <process.h>

namespace winsparkle
{

/*--------------------------------------------------------------------------*
                                 Helpers
 *--------------------------------------------------------------------------*/

namespace
{

// Sets thread's name for the debugger
void SetThreadName(DWORD threadId, const char *name)
{
#ifdef _MSC_VER
    // This code is copied verbatim from MSDN, see
    // http://msdn.microsoft.com/en-us/library/xcb2z8hs%28VS.100%29.aspx

    #define MS_VC_EXCEPTION 0x406D1388

    #pragma pack(push,8)
    typedef struct tagTHREADNAME_INFO
    {
       DWORD dwType; // Must be 0x1000.
       LPCSTR szName; // Pointer to name (in user addr space).
       DWORD dwThreadID; // Thread ID (-1=caller thread).
       DWORD dwFlags; // Reserved for future use, must be zero.
    } THREADNAME_INFO;
    #pragma pack(pop)

    Sleep(10);
    THREADNAME_INFO info;
    info.dwType = 0x1000;
    info.szName = name;
    info.dwThreadID = threadId;
    info.dwFlags = 0;

    __try
    {
        RaiseException
        (
            MS_VC_EXCEPTION,
            0,
            sizeof(info) / sizeof(ULONG_PTR),
            (ULONG_PTR*)&info
        );
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
    }
#endif // _MSC_VER
}

} // anonymous namespace

/*--------------------------------------------------------------------------*
                              Thread class
 *--------------------------------------------------------------------------*/

Thread::Thread(const char *name)
    : m_handle(NULL), m_id(0), m_signalEvent(NULL)
{
    m_handle = (HANDLE)_beginthreadex
                       (
                           NULL,                      // default security
                           0,                         // default stack size
                           &Thread::ThreadEntryPoint,
                           this,                      // arguments
                           CREATE_SUSPENDED,
                           &m_id                      // thread ID
                       );

    if ( !m_handle )
        throw Win32Exception();
}

Thread::~Thread()
{
    if ( m_handle )
        CloseHandle(m_handle);
}

/*static*/ unsigned __stdcall Thread::ThreadEntryPoint(void *data)
{
    Thread *thread = reinterpret_cast<Thread*>(data);
    thread->Run();

    if ( !thread->IsJoinable() )
        delete thread;

	return 0;
}

void Thread::Start()
{
    if ( !m_handle )
        throw Win32Exception();

    m_signalEvent = CreateEvent
                    (
                        NULL,  // default security attributes
                        FALSE, // auto-reset
                        FALSE, // initially non-signaled
                        NULL   // anonymous
                    );
    if ( !m_signalEvent )
        throw Win32Exception();

    if ( ResumeThread(m_handle) == (DWORD)-1 )
    {
        CloseHandle(m_signalEvent);
        m_signalEvent = NULL;
        throw Win32Exception();
    }

    // Wait until Run() signals that it is fully initialized.
    WaitForSingleObject(m_signalEvent, INFINITE);
    CloseHandle(m_signalEvent);
    m_signalEvent = NULL;
}

void Thread::SignalReady()
{
    SetEvent(m_signalEvent);
}

} // namespace winsparkle
<commit_msg>Forgot to actually set thread's name in ctor.<commit_after>/*
 *  This file is part of WinSparkle (http://winsparkle.org)
 *
 *  Copyright (C) 2009 Vaclav Slavik
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a
 *  copy of this software and associated documentation files (the "Software"),
 *  to deal in the Software without restriction, including without limitation
 *  the rights to use, copy, modify, merge, publish, distribute, sublicense,
 *  and/or sell copies of the Software, and to permit persons to whom the
 *  Software is furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 *  DEALINGS IN THE SOFTWARE.
 *
 */

#include "threads.h"
#include "error.h"

#include <windows.h>
#include <process.h>

namespace winsparkle
{

/*--------------------------------------------------------------------------*
                                 Helpers
 *--------------------------------------------------------------------------*/

namespace
{

// Sets thread's name for the debugger
void SetThreadName(DWORD threadId, const char *name)
{
#ifdef _MSC_VER
    // This code is copied verbatim from MSDN, see
    // http://msdn.microsoft.com/en-us/library/xcb2z8hs%28VS.100%29.aspx

    #define MS_VC_EXCEPTION 0x406D1388

    #pragma pack(push,8)
    typedef struct tagTHREADNAME_INFO
    {
       DWORD dwType; // Must be 0x1000.
       LPCSTR szName; // Pointer to name (in user addr space).
       DWORD dwThreadID; // Thread ID (-1=caller thread).
       DWORD dwFlags; // Reserved for future use, must be zero.
    } THREADNAME_INFO;
    #pragma pack(pop)

    Sleep(10);
    THREADNAME_INFO info;
    info.dwType = 0x1000;
    info.szName = name;
    info.dwThreadID = threadId;
    info.dwFlags = 0;

    __try
    {
        RaiseException
        (
            MS_VC_EXCEPTION,
            0,
            sizeof(info) / sizeof(ULONG_PTR),
            (ULONG_PTR*)&info
        );
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
    }
#endif // _MSC_VER
}

} // anonymous namespace

/*--------------------------------------------------------------------------*
                              Thread class
 *--------------------------------------------------------------------------*/

Thread::Thread(const char *name)
    : m_handle(NULL), m_id(0), m_signalEvent(NULL)
{
    m_handle = (HANDLE)_beginthreadex
                       (
                           NULL,                      // default security
                           0,                         // default stack size
                           &Thread::ThreadEntryPoint,
                           this,                      // arguments
                           CREATE_SUSPENDED,
                           &m_id                      // thread ID
                       );

    if ( !m_handle )
        throw Win32Exception();

    SetThreadName(m_id, name);
}

Thread::~Thread()
{
    if ( m_handle )
        CloseHandle(m_handle);
}

/*static*/ unsigned __stdcall Thread::ThreadEntryPoint(void *data)
{
    Thread *thread = reinterpret_cast<Thread*>(data);
    thread->Run();

    if ( !thread->IsJoinable() )
        delete thread;

	return 0;
}

void Thread::Start()
{
    if ( !m_handle )
        throw Win32Exception();

    m_signalEvent = CreateEvent
                    (
                        NULL,  // default security attributes
                        FALSE, // auto-reset
                        FALSE, // initially non-signaled
                        NULL   // anonymous
                    );
    if ( !m_signalEvent )
        throw Win32Exception();

    if ( ResumeThread(m_handle) == (DWORD)-1 )
    {
        CloseHandle(m_signalEvent);
        m_signalEvent = NULL;
        throw Win32Exception();
    }

    // Wait until Run() signals that it is fully initialized.
    WaitForSingleObject(m_signalEvent, INFINITE);
    CloseHandle(m_signalEvent);
    m_signalEvent = NULL;
}

void Thread::SignalReady()
{
    SetEvent(m_signalEvent);
}

} // namespace winsparkle
<|endoftext|>