<commit_before>#include "upperlayer.hpp"

#include <utility>
#include <vector>
#include <ostream>
#include <cassert>
#include <initializer_list>
#include <chrono>

#include <boost/asio.hpp>

#include "upperlayer_properties.hpp"

namespace upperlayer
{
namespace
{
using uchar = unsigned char;

std::size_t be_char_to_16b(std::vector<uchar> bs)
{
   assert(bs.size() == 2);
   std::size_t sz = 0;
   sz |= (static_cast<std::size_t>(bs[0]) << 8);
   sz |= (static_cast<std::size_t>(bs[1]));
   return sz;
}
std::size_t be_char_to_32b(std::vector<uchar> bs)
{
   assert(bs.size() == 4);
   std::size_t sz = 0;
   sz |= (static_cast<std::size_t>(bs[0]) << 24);
   sz |= (static_cast<std::size_t>(bs[1]) << 16);
   sz |= (static_cast<std::size_t>(bs[2]) << 8);
   sz |= (static_cast<std::size_t>(bs[3]));
   return sz;
}

}

Istate_trans_ops::~Istate_trans_ops()
{
}

scx::scx(std::initializer_list<std::pair<TYPE, std::function<void(scx*, std::unique_ptr<property>)>>> l):
   statem {this},
   handlers {}
{
   for (const auto p : l) {
      handlers[p.first] = p.second;
   }
}

scx::~scx()
{
}

void scx::send(property* p)
{
   auto pdu = std::make_shared<std::vector<unsigned char>>(p->make_pdu());
   auto ptype = get_type(*pdu);

   statemachine::EVENT e;
   switch (ptype) {
      case TYPE::A_ABORT:
         e = statemachine::EVENT::LOCL_A_ABORT_PDU;
         break;
      case TYPE::A_ASSOCIATE_AC:
         e = statemachine::EVENT::LOCL_A_ASSOCIATE_AC_PDU;
         break;
      case TYPE::A_ASSOCIATE_RJ:
         e = statemachine::EVENT::LOCL_A_ASSOCIATE_RJ_PDU;
         break;
      case TYPE::A_ASSOCIATE_RQ:
         e = statemachine::EVENT::LOCL_A_ASSOCIATE_RJ_PDU;
         break;
      case TYPE::A_RELEASE_RQ:
         e = statemachine::EVENT::LOCL_A_RELEASE_RQ_PDU;
         break;
      case TYPE::A_RELEASE_RP:
         e = statemachine::EVENT::LOCL_A_RELEASE_RP_PDU;
         break;
      case TYPE::P_DATA_TF:
         e = statemachine::EVENT::LOCL_P_DATA_TF_PDU;
         break;
      default:
         e = statemachine::EVENT::UNRECOG_PDU;
   }

   if (statem.transition(e) != statemachine::CONN_STATE::INV) {
      boost::asio::async_write(sock(), boost::asio::buffer(*pdu),
         [=](const boost::system::error_code& error, std::size_t bytes) {
            send_queue.pop();
            if (!send_queue.empty()) {
               send(send_queue.back().get());
            }
         }
      );
   }
}

void scx::do_read()
{
   auto size = std::make_shared<std::vector<unsigned char>>(6);
   auto rem_data = std::make_shared<std::vector<unsigned char>>();
   auto compl_data = std::make_shared<std::vector<unsigned char>>();
   boost::asio::async_read(sock(), boost::asio::buffer(*size), boost::asio::transfer_exactly(6),
      [=](const boost::system::error_code& error, std::size_t bytes)  {
         assert(bytes == 6);

         std::size_t len = be_char_to_32b({size->begin()+2, size->begin()+6});
         rem_data->resize(len);
         boost::asio::async_read(sock(), boost::asio::buffer(*rem_data), boost::asio::transfer_exactly(len),
            [=](const boost::system::error_code& error, std::size_t bytes) {

               compl_data->reserve(size->size() + rem_data->size());
               compl_data->insert(compl_data->end(), size->begin(), size->end());
               compl_data->insert(compl_data->end(), rem_data->begin(), rem_data->end());
               auto ptype = get_type(*compl_data);

               statemachine::EVENT e;
               switch (ptype) {
                  case TYPE::A_ABORT:
                     e = statemachine::EVENT::RECV_A_ABORT_PDU;
                     break;
                  case TYPE::A_ASSOCIATE_AC:
                     e = statemachine::EVENT::RECV_A_ASSOCIATE_AC_PDU;
                     break;
                  case TYPE::A_ASSOCIATE_RJ:
                     e = statemachine::EVENT::RECV_A_ASSOCIATE_RJ_PDU;
                     break;
                  case TYPE::A_ASSOCIATE_RQ:
                     e = statemachine::EVENT::RECV_A_ASSOCIATE_RQ_PDU;
                     break;
                  case TYPE::A_RELEASE_RQ:
                     e = statemachine::EVENT::RECV_A_RELEASE_RQ_PDU;
                     break;
                  case TYPE::A_RELEASE_RP:
                     e = statemachine::EVENT::RECV_A_RELEASE_RP_PDU;
                     break;
                  case TYPE::P_DATA_TF:
                     e = statemachine::EVENT::RECV_P_DATA_TF_PDU;
                     break;
                  default:
                     e = statemachine::EVENT::UNRECOG_PDU;
               }
               statem.transition(e);

               // call appropriate handler
//               if (statem.process_next) {
                  handlers[ptype](this, make_property(*compl_data));
//               } else {
//                  statem.process_next = true; //reset
//               }

               if (get_state() == statemachine::CONN_STATE::STA13) {
                  io_s().stop();
                  return;
               }

               // be ready for new incoming data
               if (get_state() != statemachine::CONN_STATE::STA2) {
                  //do_read();
               }
               do_read();
            }
         );
      }
   );
}

void scx::queue_for_write(std::unique_ptr<property> p)
{
   send_queue.emplace(std::move(p));
   if (send_queue.size() > 1) {
      // there are still active writes
      return;
   }
   send(send_queue.back().get());
}

void scx::run()
{
   io_s().run();
}

void scx::artim_expired(const boost::system::error_code& error)
{
   if (error != boost::asio::error::operation_aborted) {
      statem.transition(statemachine::EVENT::ARTIM_EXPIRED);
   }
}

void scx::inject(TYPE t, std::function<void (scx*, std::unique_ptr<property>)> f)
{
   handlers[t] = f;
}

statemachine::CONN_STATE scx::get_state()
{
   return statem.get_state();
}

scp::scp(short port, std::initializer_list<std::pair<TYPE, std::function<void(scx*, std::unique_ptr<property>)>>> l):
   scx {l},
   io_service(),
   socket(io_service),
   acptr(io_service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)),
   artim(io_service, std::chrono::steady_clock::now() + std::chrono::seconds(10))
{
   acptr.async_accept(socket, [=](boost::system::error_code ec) {
         if (!ec) {
            statem.transition(statemachine::EVENT::TRANS_CONN_INDIC);
            do_read();
         }
      } );
}

scu::scu(std::string host, std::string port, std::initializer_list<std::pair<TYPE, std::function<void(scx*, std::unique_ptr<property>)>>> l):
   scx{l},
   io_service(),
   resolver(io_service),
   query(host, port),
   endpoint_iterator(resolver.resolve(query)),
   socket(io_service),
   artim(io_service, std::chrono::steady_clock::now() + std::chrono::seconds(10))
{
   statem.transition(statemachine::EVENT::A_ASSOCIATE_RQ);
   boost::asio::ip::tcp::resolver::iterator end;
   boost::system::error_code error = boost::asio::error::host_not_found;
   while(error && endpoint_iterator != end)
   {
     socket.close();
     socket.connect(*endpoint_iterator++, error);
   }
   statem.transition(statemachine::EVENT::TRANS_CONN_CONF);

   assert(!error);
}

boost::asio::ip::tcp::socket& scp::sock()
{
   return socket;
}

boost::asio::io_service&scp::io_s()
{
   return io_service;
}

boost::asio::steady_timer&scp::artim_timer()
{
   return artim;
}

scp::~scp()
{
   statem.transition(statemachine::EVENT::TRANS_CONN_CLOSED);
}

boost::asio::ip::tcp::socket& scu::sock()
{
   return socket;
}

boost::asio::io_service& scu::io_s()
{
   return io_service;
}

boost::asio::steady_timer&scu::artim_timer()
{
   return artim;
}

scu::~scu()
{
   statem.transition(statemachine::EVENT::TRANS_CONN_CLOSED);
}

}

void upperlayer::scx::reset_artim()
{
}

void upperlayer::scx::stop_artim()
{
}

void upperlayer::scx::start_artim()
{
}

void upperlayer::scx::ignore_next()
{
}
<commit_msg>implemented ARTIM timer member functions used by state machine<commit_after>#include "upperlayer.hpp"

#include <utility>
#include <vector>
#include <ostream>
#include <cassert>
#include <initializer_list>
#include <chrono>

#include <boost/asio.hpp>

#include "upperlayer_properties.hpp"

namespace upperlayer
{
namespace
{
using uchar = unsigned char;

std::size_t be_char_to_16b(std::vector<uchar> bs)
{
   assert(bs.size() == 2);
   std::size_t sz = 0;
   sz |= (static_cast<std::size_t>(bs[0]) << 8);
   sz |= (static_cast<std::size_t>(bs[1]));
   return sz;
}
std::size_t be_char_to_32b(std::vector<uchar> bs)
{
   assert(bs.size() == 4);
   std::size_t sz = 0;
   sz |= (static_cast<std::size_t>(bs[0]) << 24);
   sz |= (static_cast<std::size_t>(bs[1]) << 16);
   sz |= (static_cast<std::size_t>(bs[2]) << 8);
   sz |= (static_cast<std::size_t>(bs[3]));
   return sz;
}

}

Istate_trans_ops::~Istate_trans_ops()
{
}

scx::scx(std::initializer_list<std::pair<TYPE, std::function<void(scx*, std::unique_ptr<property>)>>> l):
   statem {this},
   handlers {}
{
   for (const auto p : l) {
      handlers[p.first] = p.second;
   }
}

scx::~scx()
{
}

void scx::send(property* p)
{
   auto pdu = std::make_shared<std::vector<unsigned char>>(p->make_pdu());
   auto ptype = get_type(*pdu);

   statemachine::EVENT e;
   switch (ptype) {
      case TYPE::A_ABORT:
         e = statemachine::EVENT::LOCL_A_ABORT_PDU;
         break;
      case TYPE::A_ASSOCIATE_AC:
         e = statemachine::EVENT::LOCL_A_ASSOCIATE_AC_PDU;
         break;
      case TYPE::A_ASSOCIATE_RJ:
         e = statemachine::EVENT::LOCL_A_ASSOCIATE_RJ_PDU;
         break;
      case TYPE::A_ASSOCIATE_RQ:
         e = statemachine::EVENT::LOCL_A_ASSOCIATE_RJ_PDU;
         break;
      case TYPE::A_RELEASE_RQ:
         e = statemachine::EVENT::LOCL_A_RELEASE_RQ_PDU;
         break;
      case TYPE::A_RELEASE_RP:
         e = statemachine::EVENT::LOCL_A_RELEASE_RP_PDU;
         break;
      case TYPE::P_DATA_TF:
         e = statemachine::EVENT::LOCL_P_DATA_TF_PDU;
         break;
      default:
         e = statemachine::EVENT::UNRECOG_PDU;
   }

   if (statem.transition(e) != statemachine::CONN_STATE::INV) {
      boost::asio::async_write(sock(), boost::asio::buffer(*pdu),
         [=](const boost::system::error_code& error, std::size_t bytes) {
            send_queue.pop();
            if (!send_queue.empty()) {
               send(send_queue.back().get());
            }
         }
      );
   }
}

void scx::do_read()
{
   auto size = std::make_shared<std::vector<unsigned char>>(6);
   auto rem_data = std::make_shared<std::vector<unsigned char>>();
   auto compl_data = std::make_shared<std::vector<unsigned char>>();
   boost::asio::async_read(sock(), boost::asio::buffer(*size), boost::asio::transfer_exactly(6),
      [=](const boost::system::error_code& error, std::size_t bytes)  {
         assert(bytes == 6);

         std::size_t len = be_char_to_32b({size->begin()+2, size->begin()+6});
         rem_data->resize(len);
         boost::asio::async_read(sock(), boost::asio::buffer(*rem_data), boost::asio::transfer_exactly(len),
            [=](const boost::system::error_code& error, std::size_t bytes) {

               compl_data->reserve(size->size() + rem_data->size());
               compl_data->insert(compl_data->end(), size->begin(), size->end());
               compl_data->insert(compl_data->end(), rem_data->begin(), rem_data->end());
               auto ptype = get_type(*compl_data);

               statemachine::EVENT e;
               switch (ptype) {
                  case TYPE::A_ABORT:
                     e = statemachine::EVENT::RECV_A_ABORT_PDU;
                     break;
                  case TYPE::A_ASSOCIATE_AC:
                     e = statemachine::EVENT::RECV_A_ASSOCIATE_AC_PDU;
                     break;
                  case TYPE::A_ASSOCIATE_RJ:
                     e = statemachine::EVENT::RECV_A_ASSOCIATE_RJ_PDU;
                     break;
                  case TYPE::A_ASSOCIATE_RQ:
                     e = statemachine::EVENT::RECV_A_ASSOCIATE_RQ_PDU;
                     break;
                  case TYPE::A_RELEASE_RQ:
                     e = statemachine::EVENT::RECV_A_RELEASE_RQ_PDU;
                     break;
                  case TYPE::A_RELEASE_RP:
                     e = statemachine::EVENT::RECV_A_RELEASE_RP_PDU;
                     break;
                  case TYPE::P_DATA_TF:
                     e = statemachine::EVENT::RECV_P_DATA_TF_PDU;
                     break;
                  default:
                     e = statemachine::EVENT::UNRECOG_PDU;
               }
               statem.transition(e);

               // call appropriate handler
//               if (statem.process_next) {
                  handlers[ptype](this, make_property(*compl_data));
//               } else {
//                  statem.process_next = true; //reset
//               }

               if (get_state() == statemachine::CONN_STATE::STA13) {
                  io_s().stop();
                  return;
               }

               // be ready for new incoming data
               if (get_state() != statemachine::CONN_STATE::STA2) {
                  //do_read();
               }
               do_read();
            }
         );
      }
   );
}

void scx::queue_for_write(std::unique_ptr<property> p)
{
   send_queue.emplace(std::move(p));
   if (send_queue.size() > 1) {
      // there are still active writes
      return;
   }
   send(send_queue.back().get());
}

void scx::run()
{
   io_s().run();
}

void scx::artim_expired(const boost::system::error_code& error)
{
   if (error != boost::asio::error::operation_aborted) {
      statem.transition(statemachine::EVENT::ARTIM_EXPIRED);
   }
}

void scx::inject(TYPE t, std::function<void (scx*, std::unique_ptr<property>)> f)
{
   handlers[t] = f;
}

statemachine::CONN_STATE scx::get_state()
{
   return statem.get_state();
}

scp::scp(short port, std::initializer_list<std::pair<TYPE, std::function<void(scx*, std::unique_ptr<property>)>>> l):
   scx {l},
   io_service(),
   socket(io_service),
   acptr(io_service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)),
   artim(io_service, std::chrono::steady_clock::now() + std::chrono::seconds(10))
{
   acptr.async_accept(socket, [=](boost::system::error_code ec) {
         if (!ec) {
            statem.transition(statemachine::EVENT::TRANS_CONN_INDIC);
            do_read();
         }
      } );
}

scu::scu(std::string host, std::string port, std::initializer_list<std::pair<TYPE, std::function<void(scx*, std::unique_ptr<property>)>>> l):
   scx{l},
   io_service(),
   resolver(io_service),
   query(host, port),
   endpoint_iterator(resolver.resolve(query)),
   socket(io_service),
   artim(io_service, std::chrono::steady_clock::now() + std::chrono::seconds(10))
{
   statem.transition(statemachine::EVENT::A_ASSOCIATE_RQ);
   boost::asio::ip::tcp::resolver::iterator end;
   boost::system::error_code error = boost::asio::error::host_not_found;
   while(error && endpoint_iterator != end)
   {
     socket.close();
     socket.connect(*endpoint_iterator++, error);
   }
   statem.transition(statemachine::EVENT::TRANS_CONN_CONF);

   assert(!error);
}

boost::asio::ip::tcp::socket& scp::sock()
{
   return socket;
}

boost::asio::io_service&scp::io_s()
{
   return io_service;
}

boost::asio::steady_timer&scp::artim_timer()
{
   return artim;
}

scp::~scp()
{
   statem.transition(statemachine::EVENT::TRANS_CONN_CLOSED);
}

boost::asio::ip::tcp::socket& scu::sock()
{
   return socket;
}

boost::asio::io_service& scu::io_s()
{
   return io_service;
}

boost::asio::steady_timer&scu::artim_timer()
{
   return artim;
}

scu::~scu()
{
   statem.transition(statemachine::EVENT::TRANS_CONN_CLOSED);
}

}

void upperlayer::scx::reset_artim()
{
   using namespace std::placeholders;
   artim_timer().cancel();
   artim_timer().async_wait(std::bind(&scx::artim_expired, this, _1));
}

void upperlayer::scx::stop_artim()
{
   artim_timer().cancel();
}

void upperlayer::scx::start_artim()
{
   using namespace std::placeholders;
   artim_timer().async_wait(std::bind(&scx::artim_expired, this, _1));
}

void upperlayer::scx::ignore_next()
{
   //todo ?readqueue?
}
<|endoftext|>


<commit_before>/***************************************************************
 *
 * Copyright (C) 1990-2007, Condor Team, Computer Sciences Department,
 * University of Wisconsin-Madison, WI.
 *
 * 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 "condor_common.h"
#include "condor_config.h"
#include "../condor_daemon_core.V6/condor_daemon_core.h"
#include "daemon_core_sock_adapter.h"
#include "subsystem_info.h"
#include "shared_port_client.h"
#include "shared_port_endpoint.h"

#ifdef HAVE_SCM_RIGHTS_PASSFD
#include "shared_port_scm_rights.h"
#endif

bool
SharedPortClient::sendSharedPortID(char const *shared_port_id,Sock *sock)
{
		// NOTE: Even platforms that do not support USE_SHARED_PORT
		// for their own daemons support the remote client protocol
		// for accessing daemons on other platforms that are using
		// shared ports.  This function does that.  It doesn't depend
		// on anything platform-dependent.

	sock->encode();
	sock->put(SHARED_PORT_CONNECT);
	sock->put(shared_port_id);

		// for debugging
	sock->put(myName().Value());

	int deadline = sock->get_deadline();
	if( deadline ) {
		deadline -= time(NULL);
		if( deadline < 0 ) {
			deadline = 0;
		}
	}
	else {
		deadline = sock->get_timeout_raw();
		if( deadline == 0 ) {
			deadline = -1;
		}
	}
	sock->put(deadline);

		// for possible future use
	int more_args = 0;
	sock->put(more_args);

	if( !sock->end_of_message() ) {
		dprintf(D_ALWAYS,
				"SharedPortClient: failed to send target id %s to %s.\n",
				shared_port_id, sock->peer_description());
		return false;
	}

	dprintf(D_FULLDEBUG,
			"SharedPortClient: sent connection request to %s for shared port id %s\n",
			sock->peer_description(), shared_port_id);
	return true;
}

MyString
SharedPortClient::myName()
{
	// This is purely for debugging purposes.
	// It is who we say we are when talking to the shared port server.
	MyString name;
	name = get_mySubSystem()->getName();
	if( daemonCoreSockAdapter.isEnabled() ) {
		name += " ";
		name += daemonCoreSockAdapter.publicNetworkIpAddr();
	}
	return name;
}

bool
SharedPortClient::SharedPortIdIsValid(char const *shared_port_id)
{
		// Make sure the socket name does not contain any special path
		// characters that could be used to cause us to open a named
		// socket outside of our base directory.  The following is
		// overly protective and someday we might want to add support
		// for sub-directories (but still avoiding stuff like "/../").
	for(;*shared_port_id;shared_port_id++) {
		if( isalnum(*shared_port_id) || *shared_port_id == '.' || *shared_port_id == '-' || *shared_port_id == '_' ) {
			continue;
		}
		return false;
	}
	return true;
}

bool
SharedPortClient::PassSocket(Sock *sock_to_pass,char const *shared_port_id,char const *requested_by)
{
#ifndef HAVE_SHARED_PORT
	dprintf(D_ALWAYS,"SharedPortClient::PassSocket() not supported on this platform\n");
	return false;
#elif WIN32
	if( !SharedPortIdIsValid(shared_port_id) ) {
		dprintf(D_ALWAYS,
				"ERROR: SharedPortClient: refusing to connect to shared port"
				"%s, because specified id is illegal! (%s)\n",
				requested_by, shared_port_id );
		return false;
	}

	MyString pipe_name;
	MyString socket_dir;

	SharedPortEndpoint::paramDaemonSocketDir(pipe_name);
	pipe_name.sprintf_cat("%c%s",DIR_DELIM_CHAR,shared_port_id);

	MyString requested_by_buf;
	if( !requested_by ) {
		requested_by_buf.sprintf(
			" as requested by %s", sock_to_pass->peer_description());
		requested_by = requested_by_buf.Value();
	}

	HANDLE child_pipe;

	while(true)
	{
		child_pipe = CreateFile(
			pipe_name.Value(),
			GENERIC_READ | GENERIC_WRITE,
			0,
			NULL,
			OPEN_EXISTING,
			0,
			NULL);

		if(child_pipe != INVALID_HANDLE_VALUE)
			break;

		if(GetLastError() == ERROR_PIPE_BUSY)
		{
			if (!WaitNamedPipe(pipe_name.Value(), 20000))
			{
				dprintf(D_ALWAYS, "ERROR: SharedPortClient: Wait for named pipe for sending socket timed out: %d\n", GetLastError());
				return false;
			}
		}
		else
		{
			dprintf(D_ALWAYS, "ERROR: SharedPortClient: Failed to open named pipe for sending socket: %d\n", GetLastError());
			return false;
		}
	}

	DWORD child_pid;
	DWORD read_bytes = 0;

	BOOL read_result = ReadFile(child_pipe, &child_pid, sizeof(DWORD), &read_bytes, NULL);

	if(!read_result)
	{
		DWORD last_error = GetLastError();
		dprintf(D_ALWAYS, "ERROR: SharedPortClient: Failed to read PID from pipe: %d.\n", last_error);
		CloseHandle(child_pipe);
		return false;
	}
	else
	{
		dprintf(D_FULLDEBUG, "SharedPortClient: Read PID: %d\n", child_pid);
	}

	WSAPROTOCOL_INFO protocol_info;
	int dup_result = WSADuplicateSocket(sock_to_pass->get_file_desc(), child_pid, &protocol_info);
	if(dup_result == SOCKET_ERROR)
	{
		dprintf(D_ALWAYS, "ERROR: SharedPortClient: Failed to duplicate socket.\n");
		CloseHandle(child_pipe);
		return false;
	}

	int bufferSize = (sizeof(int) + sizeof(protocol_info));
	char *buffer = new char[bufferSize];
	ASSERT( buffer );
	int cmd = SHARED_PORT_PASS_SOCK;
	memcpy_s(buffer, sizeof(int), &cmd, sizeof(int));
	memcpy_s(buffer+sizeof(int), sizeof(protocol_info), &protocol_info, sizeof(protocol_info));
	BOOL write_result = WriteFile(child_pipe, buffer, bufferSize, &read_bytes, 0);

	delete [] buffer;
	if(!write_result)
	{
		dprintf(D_ALWAYS, "ERROR: SharedPortClient: Failed to send WSAPROTOCOL_INFO struct: %d\n", GetLastError());
		CloseHandle(child_pipe);
		return false;
	}
	dprintf(D_FULLDEBUG, "SharedPortClient: Wrote %d bytes to named pipe.\n", read_bytes);
	FlushFileBuffers(child_pipe);

	CloseHandle(child_pipe);

	return true;
#elif HAVE_SCM_RIGHTS_PASSFD
	if( !SharedPortIdIsValid(shared_port_id) ) {
		dprintf(D_ALWAYS,
				"ERROR: SharedPortClient: refusing to connect to shared port"
				"%s, because specified id is illegal! (%s)\n",
				requested_by, shared_port_id );
		return false;
	}

	MyString sock_name;
	MyString socket_dir;

	SharedPortEndpoint::paramDaemonSocketDir(sock_name);
	sock_name.sprintf_cat("%c%s",DIR_DELIM_CHAR,shared_port_id);

	MyString requested_by_buf;
	if( !requested_by ) {
		requested_by_buf.sprintf(
			" as requested by %s", sock_to_pass->peer_description());
		requested_by = requested_by_buf.Value();
	}

	struct sockaddr_un named_sock_addr;
	memset(&named_sock_addr, 0, sizeof(named_sock_addr));
	named_sock_addr.sun_family = AF_UNIX;
	strncpy(named_sock_addr.sun_path,sock_name.Value(),sizeof(named_sock_addr.sun_path)-1);
	if( strcmp(named_sock_addr.sun_path,sock_name.Value()) ) {
		dprintf(D_ALWAYS,"ERROR: SharedPortClient: full socket name%s is too long: %s\n",
				requested_by,
				sock_name.Value());
		return false;
	}

	int named_sock_fd = socket(AF_UNIX,SOCK_STREAM,0);
	if( named_sock_fd == -1 ) {
		dprintf(D_ALWAYS,"ERROR: SharedPortClient: failed to created named socket%s to connect to %s: %s\n",
				requested_by,
				shared_port_id,
				strerror(errno));
		return false;
	}

	ReliSock named_sock;
	named_sock.assign(named_sock_fd);
	named_sock.set_deadline( sock_to_pass->get_deadline() );

	priv_state orig_priv = set_root_priv();

	int connect_rc = connect(named_sock_fd,(struct sockaddr *)&named_sock_addr, SUN_LEN(&named_sock_addr));

	set_priv( orig_priv );

	if( connect_rc != 0 )
	{
		dprintf(D_ALWAYS,"SharedPortClient: failed to connect to %s%s: %s\n",
				sock_name.Value(),
				requested_by,
				strerror(errno));
		return false;
	}

	// Make certain SO_LINGER is Off.  This will result in the default
	// of closesocket returning immediately and the system attempts to
	// send any unsent data.

	struct linger linger = {0,0};
	setsockopt(named_sock_fd, SOL_SOCKET, SO_LINGER, (char*)&linger, sizeof(linger));

	// Now prepare to pass the file descriptor
	// to the target process via named_sock.

	// First tell the target daemon that we are about to send the fd.
	named_sock.encode();
	if( !named_sock.put((int)SHARED_PORT_PASS_SOCK) ||
		!named_sock.end_of_message() )
	{
		dprintf(D_ALWAYS,"SharedPortClient: failed to send SHARED_PORT_PASS_FD to %s%s: %s\n",
				sock_name.Value(),
				requested_by,
				strerror(errno));
		return false;
	}

	// Now send the fd.

	// The documented way to initialize msghdr is to first set msg_controllen
	// to the size of the cmsghdr buffer and then after initializing
	// cmsghdr(s) to set it to the sum of CMSG_LEN() across all cmsghdrs.

	struct msghdr msg;
	char *buf = (char *) malloc(CMSG_SPACE(sizeof(int)));
	msg.msg_name = NULL;
	msg.msg_namelen = 0;
	msg.msg_control = buf;
	msg.msg_controllen = CMSG_SPACE(sizeof(int));
	msg.msg_flags = 0;

		// I have found that on MacOS X 10.5, we must send at least 1 byte,
		// or we get "Message too long" when trying to send 0-byte message.
	struct iovec iov[1];
	int junk = 0;
	iov[0].iov_base = &junk;
	iov[0].iov_len = 1;
	msg.msg_iov = iov;
	msg.msg_iovlen = 1;

	struct cmsghdr *cmsg = CMSG_FIRSTHDR((&msg));
	void *cmsg_data = CMSG_DATA(cmsg);
	ASSERT( cmsg && cmsg_data );

	cmsg->cmsg_len = CMSG_LEN(sizeof(int));
	cmsg->cmsg_level = SOL_SOCKET;
	cmsg->cmsg_type = SCM_RIGHTS;

	int fd_to_pass = sock_to_pass->get_file_desc();
	memcpy(cmsg_data,&fd_to_pass,sizeof(int));

	msg.msg_controllen = cmsg->cmsg_len;

	if( sendmsg(named_sock.get_file_desc(),&msg,0) != 1 ) {
		dprintf(D_ALWAYS,"SharedPortClient: failed to pass socket to %s%s: %s\n",
				sock_name.Value(),
				requested_by,
				strerror(errno));
		free(buf);
		return false;
	}

		// The following final ACK appears to be necessary on Mac OS X
		// 10.5.8 (not sure of others).  It does not appear to be
		// necessary on any version of linux that I have tried.  The
		// observed problem that this solves is random failures where
		// the endpoint sees the passed socket close right away.  It
		// would be nice not to have an ACK, because then the whole
		// PassSocket() protocol would be non-blocking.  Since the
		// protocol is blocking with this ACK, it means we could
		// temporarily deadlock if the endpoint we are passing the
		// socket to is blocking on us on some other channel.  If
		// this becomes a problem, we can at least make the ACK only
		// happen on platforms that need it.  This protocol is always
		// just local to the machine, so need to worry about keeping
		// it compatible between different platforms.

	named_sock.decode();
	int status = 0;
	if( !named_sock.get(status) || !named_sock.end_of_message() ) {
		dprintf(D_ALWAYS,"SharedPortClient: failed to receive result for SHARED_PORT_PASS_FD to %s%s: %s\n",
				sock_name.Value(),
				requested_by,
				strerror(errno));

		free(buf);
		return false;
	}
	if( status != 0 ) {
		dprintf(D_ALWAYS,"SharedPortClient: received failure response for SHARED_PORT_PASS_FD to %s%s\n",
				sock_name.Value(),
				requested_by);
		free(buf);
		return false;
	}

	dprintf(D_FULLDEBUG,"SharedPortClient: passed socket to %s%s\n",
			sock_name.Value(),
			requested_by);
	free(buf);
	return true;
#else
#error HAVE_SHARED_PORT is defined, but no method for passing fds is enabled.
#endif
}
<commit_msg>refactor windows version of SHARED_PORT_PASS_SOCK command to fix a warning from MSVC_ANALYZE. ===VersionHistory:None===<commit_after>/***************************************************************
 *
 * Copyright (C) 1990-2007, Condor Team, Computer Sciences Department,
 * University of Wisconsin-Madison, WI.
 *
 * 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 "condor_common.h"
#include "condor_config.h"
#include "../condor_daemon_core.V6/condor_daemon_core.h"
#include "daemon_core_sock_adapter.h"
#include "subsystem_info.h"
#include "shared_port_client.h"
#include "shared_port_endpoint.h"

#ifdef HAVE_SCM_RIGHTS_PASSFD
#include "shared_port_scm_rights.h"
#endif

bool
SharedPortClient::sendSharedPortID(char const *shared_port_id,Sock *sock)
{
		// NOTE: Even platforms that do not support USE_SHARED_PORT
		// for their own daemons support the remote client protocol
		// for accessing daemons on other platforms that are using
		// shared ports.  This function does that.  It doesn't depend
		// on anything platform-dependent.

	sock->encode();
	sock->put(SHARED_PORT_CONNECT);
	sock->put(shared_port_id);

		// for debugging
	sock->put(myName().Value());

	int deadline = sock->get_deadline();
	if( deadline ) {
		deadline -= time(NULL);
		if( deadline < 0 ) {
			deadline = 0;
		}
	}
	else {
		deadline = sock->get_timeout_raw();
		if( deadline == 0 ) {
			deadline = -1;
		}
	}
	sock->put(deadline);

		// for possible future use
	int more_args = 0;
	sock->put(more_args);

	if( !sock->end_of_message() ) {
		dprintf(D_ALWAYS,
				"SharedPortClient: failed to send target id %s to %s.\n",
				shared_port_id, sock->peer_description());
		return false;
	}

	dprintf(D_FULLDEBUG,
			"SharedPortClient: sent connection request to %s for shared port id %s\n",
			sock->peer_description(), shared_port_id);
	return true;
}

MyString
SharedPortClient::myName()
{
	// This is purely for debugging purposes.
	// It is who we say we are when talking to the shared port server.
	MyString name;
	name = get_mySubSystem()->getName();
	if( daemonCoreSockAdapter.isEnabled() ) {
		name += " ";
		name += daemonCoreSockAdapter.publicNetworkIpAddr();
	}
	return name;
}

bool
SharedPortClient::SharedPortIdIsValid(char const *shared_port_id)
{
		// Make sure the socket name does not contain any special path
		// characters that could be used to cause us to open a named
		// socket outside of our base directory.  The following is
		// overly protective and someday we might want to add support
		// for sub-directories (but still avoiding stuff like "/../").
	for(;*shared_port_id;shared_port_id++) {
		if( isalnum(*shared_port_id) || *shared_port_id == '.' || *shared_port_id == '-' || *shared_port_id == '_' ) {
			continue;
		}
		return false;
	}
	return true;
}

bool
SharedPortClient::PassSocket(Sock *sock_to_pass,char const *shared_port_id,char const *requested_by)
{
#ifndef HAVE_SHARED_PORT
	dprintf(D_ALWAYS,"SharedPortClient::PassSocket() not supported on this platform\n");
	return false;
#elif WIN32
	if( !SharedPortIdIsValid(shared_port_id) ) {
		dprintf(D_ALWAYS,
				"ERROR: SharedPortClient: refusing to connect to shared port"
				"%s, because specified id is illegal! (%s)\n",
				requested_by, shared_port_id );
		return false;
	}

	MyString pipe_name;
	MyString socket_dir;

	SharedPortEndpoint::paramDaemonSocketDir(pipe_name);
	pipe_name.sprintf_cat("%c%s",DIR_DELIM_CHAR,shared_port_id);

	MyString requested_by_buf;
	if( !requested_by ) {
		requested_by_buf.sprintf(
			" as requested by %s", sock_to_pass->peer_description());
		requested_by = requested_by_buf.Value();
	}

	HANDLE child_pipe;

	while(true)
	{
		child_pipe = CreateFile(
			pipe_name.Value(),
			GENERIC_READ | GENERIC_WRITE,
			0,
			NULL,
			OPEN_EXISTING,
			0,
			NULL);

		if(child_pipe != INVALID_HANDLE_VALUE)
			break;

		if(GetLastError() == ERROR_PIPE_BUSY)
		{
			if (!WaitNamedPipe(pipe_name.Value(), 20000))
			{
				dprintf(D_ALWAYS, "ERROR: SharedPortClient: Wait for named pipe for sending socket timed out: %d\n", GetLastError());
				return false;
			}
		}
		else
		{
			dprintf(D_ALWAYS, "ERROR: SharedPortClient: Failed to open named pipe for sending socket: %d\n", GetLastError());
			return false;
		}
	}

	DWORD child_pid;
	DWORD read_bytes = 0;

	BOOL read_result = ReadFile(child_pipe, &child_pid, sizeof(DWORD), &read_bytes, NULL);

	if(!read_result)
	{
		DWORD last_error = GetLastError();
		dprintf(D_ALWAYS, "ERROR: SharedPortClient: Failed to read PID from pipe: %d.\n", last_error);
		CloseHandle(child_pipe);
		return false;
	}
	else
	{
		dprintf(D_FULLDEBUG, "SharedPortClient: Read PID: %d\n", child_pid);
	}

#if 1  // tj:2012 kill the else block later
	#pragma pack(push, 4)
	struct {
		int id; // condor commmand id
		WSAPROTOCOL_INFO wsa; // payload.
	} protocol_command;
	#pragma pack(pop)
	ZeroMemory(&protocol_command, sizeof(protocol_command));

	int dup_result = WSADuplicateSocket(sock_to_pass->get_file_desc(), child_pid, &protocol_command.wsa);
	if(dup_result == SOCKET_ERROR)
	{
		dprintf(D_ALWAYS, "ERROR: SharedPortClient: Failed to duplicate socket.\n");
		CloseHandle(child_pipe);
		return false;
	}
	protocol_command.id = SHARED_PORT_PASS_SOCK;
	BOOL write_result = WriteFile(child_pipe, &protocol_command, sizeof(protocol_command), &read_bytes, 0);
#else
	WSAPROTOCOL_INFO protocol_info;
	int dup_result = WSADuplicateSocket(sock_to_pass->get_file_desc(), child_pid, &protocol_info);
	if(dup_result == SOCKET_ERROR)
	{
		dprintf(D_ALWAYS, "ERROR: SharedPortClient: Failed to duplicate socket.\n");
		CloseHandle(child_pipe);
		return false;
	}
	int bufferSize = (sizeof(int) + sizeof(protocol_info));
	char *buffer = new char[bufferSize];
	ASSERT( buffer );
	int cmd = SHARED_PORT_PASS_SOCK;
	memcpy_s(buffer, sizeof(int), &cmd, sizeof(int));
	memcpy_s(buffer+sizeof(int), sizeof(protocol_info), &protocol_info, sizeof(protocol_info));
	BOOL write_result = WriteFile(child_pipe, buffer, bufferSize, &read_bytes, 0);

	delete [] buffer;
#endif
	if(!write_result)
	{
		dprintf(D_ALWAYS, "ERROR: SharedPortClient: Failed to send WSAPROTOCOL_INFO struct: %d\n", GetLastError());
		CloseHandle(child_pipe);
		return false;
	}
	dprintf(D_FULLDEBUG, "SharedPortClient: Wrote %d bytes to named pipe.\n", read_bytes);
	FlushFileBuffers(child_pipe);

	CloseHandle(child_pipe);

	return true;
#elif HAVE_SCM_RIGHTS_PASSFD
	if( !SharedPortIdIsValid(shared_port_id) ) {
		dprintf(D_ALWAYS,
				"ERROR: SharedPortClient: refusing to connect to shared port"
				"%s, because specified id is illegal! (%s)\n",
				requested_by, shared_port_id );
		return false;
	}

	MyString sock_name;
	MyString socket_dir;

	SharedPortEndpoint::paramDaemonSocketDir(sock_name);
	sock_name.sprintf_cat("%c%s",DIR_DELIM_CHAR,shared_port_id);

	MyString requested_by_buf;
	if( !requested_by ) {
		requested_by_buf.sprintf(
			" as requested by %s", sock_to_pass->peer_description());
		requested_by = requested_by_buf.Value();
	}

	struct sockaddr_un named_sock_addr;
	memset(&named_sock_addr, 0, sizeof(named_sock_addr));
	named_sock_addr.sun_family = AF_UNIX;
	strncpy(named_sock_addr.sun_path,sock_name.Value(),sizeof(named_sock_addr.sun_path)-1);
	if( strcmp(named_sock_addr.sun_path,sock_name.Value()) ) {
		dprintf(D_ALWAYS,"ERROR: SharedPortClient: full socket name%s is too long: %s\n",
				requested_by,
				sock_name.Value());
		return false;
	}

	int named_sock_fd = socket(AF_UNIX,SOCK_STREAM,0);
	if( named_sock_fd == -1 ) {
		dprintf(D_ALWAYS,"ERROR: SharedPortClient: failed to created named socket%s to connect to %s: %s\n",
				requested_by,
				shared_port_id,
				strerror(errno));
		return false;
	}

	ReliSock named_sock;
	named_sock.assign(named_sock_fd);
	named_sock.set_deadline( sock_to_pass->get_deadline() );

	priv_state orig_priv = set_root_priv();

	int connect_rc = connect(named_sock_fd,(struct sockaddr *)&named_sock_addr, SUN_LEN(&named_sock_addr));

	set_priv( orig_priv );

	if( connect_rc != 0 )
	{
		dprintf(D_ALWAYS,"SharedPortClient: failed to connect to %s%s: %s\n",
				sock_name.Value(),
				requested_by,
				strerror(errno));
		return false;
	}

	// Make certain SO_LINGER is Off.  This will result in the default
	// of closesocket returning immediately and the system attempts to
	// send any unsent data.

	struct linger linger = {0,0};
	setsockopt(named_sock_fd, SOL_SOCKET, SO_LINGER, (char*)&linger, sizeof(linger));

	// Now prepare to pass the file descriptor
	// to the target process via named_sock.

	// First tell the target daemon that we are about to send the fd.
	named_sock.encode();
	if( !named_sock.put((int)SHARED_PORT_PASS_SOCK) ||
		!named_sock.end_of_message() )
	{
		dprintf(D_ALWAYS,"SharedPortClient: failed to send SHARED_PORT_PASS_FD to %s%s: %s\n",
				sock_name.Value(),
				requested_by,
				strerror(errno));
		return false;
	}

	// Now send the fd.

	// The documented way to initialize msghdr is to first set msg_controllen
	// to the size of the cmsghdr buffer and then after initializing
	// cmsghdr(s) to set it to the sum of CMSG_LEN() across all cmsghdrs.

	struct msghdr msg;
	char *buf = (char *) malloc(CMSG_SPACE(sizeof(int)));
	msg.msg_name = NULL;
	msg.msg_namelen = 0;
	msg.msg_control = buf;
	msg.msg_controllen = CMSG_SPACE(sizeof(int));
	msg.msg_flags = 0;

		// I have found that on MacOS X 10.5, we must send at least 1 byte,
		// or we get "Message too long" when trying to send 0-byte message.
	struct iovec iov[1];
	int junk = 0;
	iov[0].iov_base = &junk;
	iov[0].iov_len = 1;
	msg.msg_iov = iov;
	msg.msg_iovlen = 1;

	struct cmsghdr *cmsg = CMSG_FIRSTHDR((&msg));
	void *cmsg_data = CMSG_DATA(cmsg);
	ASSERT( cmsg && cmsg_data );

	cmsg->cmsg_len = CMSG_LEN(sizeof(int));
	cmsg->cmsg_level = SOL_SOCKET;
	cmsg->cmsg_type = SCM_RIGHTS;

	int fd_to_pass = sock_to_pass->get_file_desc();
	memcpy(cmsg_data,&fd_to_pass,sizeof(int));

	msg.msg_controllen = cmsg->cmsg_len;

	if( sendmsg(named_sock.get_file_desc(),&msg,0) != 1 ) {
		dprintf(D_ALWAYS,"SharedPortClient: failed to pass socket to %s%s: %s\n",
				sock_name.Value(),
				requested_by,
				strerror(errno));
		free(buf);
		return false;
	}

		// The following final ACK appears to be necessary on Mac OS X
		// 10.5.8 (not sure of others).  It does not appear to be
		// necessary on any version of linux that I have tried.  The
		// observed problem that this solves is random failures where
		// the endpoint sees the passed socket close right away.  It
		// would be nice not to have an ACK, because then the whole
		// PassSocket() protocol would be non-blocking.  Since the
		// protocol is blocking with this ACK, it means we could
		// temporarily deadlock if the endpoint we are passing the
		// socket to is blocking on us on some other channel.  If
		// this becomes a problem, we can at least make the ACK only
		// happen on platforms that need it.  This protocol is always
		// just local to the machine, so need to worry about keeping
		// it compatible between different platforms.

	named_sock.decode();
	int status = 0;
	if( !named_sock.get(status) || !named_sock.end_of_message() ) {
		dprintf(D_ALWAYS,"SharedPortClient: failed to receive result for SHARED_PORT_PASS_FD to %s%s: %s\n",
				sock_name.Value(),
				requested_by,
				strerror(errno));

		free(buf);
		return false;
	}
	if( status != 0 ) {
		dprintf(D_ALWAYS,"SharedPortClient: received failure response for SHARED_PORT_PASS_FD to %s%s\n",
				sock_name.Value(),
				requested_by);
		free(buf);
		return false;
	}

	dprintf(D_FULLDEBUG,"SharedPortClient: passed socket to %s%s\n",
			sock_name.Value(),
			requested_by);
	free(buf);
	return true;
#else
#error HAVE_SHARED_PORT is defined, but no method for passing fds is enabled.
#endif
}
<|endoftext|>


<commit_before>#ifndef ITERBASE_HPP_
#define ITERBASE_HPP_

// This file consists of utilities used for the generic nature of the
// iterable wrapper classes.  As such, the contents of this file should be
// considered UNDOCUMENTED and is subject to change without warning.  This
// also applies to the name of the file.  No user code should include
// this file directly.

#include <utility>
#include <iterator>
#include <functional>
#include <cstddef>

namespace iter {
    // because std::advance assumes a lot and is actually smart, I need a dumb
    // version that will work with most things
    template <typename InputIt, typename Distance =std::size_t>
    void dumb_advance(InputIt& iter, Distance distance=1) {
        for (Distance i(0); i < distance; ++i) {
            ++iter;
        }
    }

    // iter will not be incremented past end
    template <typename InputIt, typename Distance =std::size_t>
    void dumb_advance(InputIt& iter, const InputIt& end, Distance distance=1) {
        for (Distance i(0); i < distance && iter != end; ++i) {
            ++iter;
        }
    }

    template <typename ForwardIt, typename Distance =std::size_t>
    ForwardIt dumb_next(ForwardIt it, Distance distance=1) {
        dumb_advance(it, distance);
        return it;
    }

    template <typename ForwardIt, typename Distance =std::size_t>
    ForwardIt dumb_next(
            ForwardIt it, const ForwardIt& end, Distance distance=1) {
        dumb_advance(it, end, distance);
        return it;
    }

    // iterator_type<C> is the type of C's iterator
    template <typename Container>
    using iterator_type =
        decltype(std::begin(std::declval<Container&>()));

    // iterator_deref<C> is the type obtained by dereferencing an iterator
    // to an object of type C
    template <typename Container>
    using iterator_deref =
        decltype(*std::declval<iterator_type<Container>&>());

    // iterator_type<C> is the type of C's iterator
    template <typename Container>
    using reverse_iterator_type =
        decltype(std::declval<Container&>().rbegin());

    // iterator_deref<C> is the type obtained by dereferencing an iterator
    // to an object of type C
    template <typename Container>
    using reverse_iterator_deref =
        decltype(*std::declval<reverse_iterator_type<Container>&>());

    // For combinatoric functions, if the Containers iterator dereferences
    // to a reference, then this is a std::reference_wrapper for that type
    // otherwise it's a non-const of that type
    template <typename Container>
    using collection_item_type =
        typename std::conditional<
        std::is_reference<iterator_deref<Container>>::value,
        std::reference_wrapper<
            typename std::remove_reference<
            iterator_deref<Container>>::type>,
        typename std::remove_const<
            iterator_deref<Container>>::type>::type;

    template <typename... Ts>
    struct are_same {
        constexpr static bool value = true;
    };

    template <typename T, typename U, typename... Ts>
    struct are_same<T, U, Ts...> {
        constexpr static bool value =
            std::is_same<T, U>::value && are_same<T, Ts...>::value;
    };

}

#endif // #ifndef ITERBASE_HPP_
<commit_msg>better template meta programming for are_same<commit_after>#ifndef ITERBASE_HPP_
#define ITERBASE_HPP_

// This file consists of utilities used for the generic nature of the
// iterable wrapper classes.  As such, the contents of this file should be
// considered UNDOCUMENTED and is subject to change without warning.  This
// also applies to the name of the file.  No user code should include
// this file directly.

#include <utility>
#include <iterator>
#include <functional>
#include <cstddef>
#include <type_traits>

namespace iter {
    // because std::advance assumes a lot and is actually smart, I need a dumb
    // version that will work with most things
    template <typename InputIt, typename Distance =std::size_t>
    void dumb_advance(InputIt& iter, Distance distance=1) {
        for (Distance i(0); i < distance; ++i) {
            ++iter;
        }
    }

    // iter will not be incremented past end
    template <typename InputIt, typename Distance =std::size_t>
    void dumb_advance(InputIt& iter, const InputIt& end, Distance distance=1) {
        for (Distance i(0); i < distance && iter != end; ++i) {
            ++iter;
        }
    }

    template <typename ForwardIt, typename Distance =std::size_t>
    ForwardIt dumb_next(ForwardIt it, Distance distance=1) {
        dumb_advance(it, distance);
        return it;
    }

    template <typename ForwardIt, typename Distance =std::size_t>
    ForwardIt dumb_next(
            ForwardIt it, const ForwardIt& end, Distance distance=1) {
        dumb_advance(it, end, distance);
        return it;
    }

    // iterator_type<C> is the type of C's iterator
    template <typename Container>
    using iterator_type =
        decltype(std::begin(std::declval<Container&>()));

    // iterator_deref<C> is the type obtained by dereferencing an iterator
    // to an object of type C
    template <typename Container>
    using iterator_deref =
        decltype(*std::declval<iterator_type<Container>&>());

    // iterator_type<C> is the type of C's iterator
    template <typename Container>
    using reverse_iterator_type =
        decltype(std::declval<Container&>().rbegin());

    // iterator_deref<C> is the type obtained by dereferencing an iterator
    // to an object of type C
    template <typename Container>
    using reverse_iterator_deref =
        decltype(*std::declval<reverse_iterator_type<Container>&>());

    // For combinatoric functions, if the Containers iterator dereferences
    // to a reference, then this is a std::reference_wrapper for that type
    // otherwise it's a non-const of that type
    template <typename Container>
    using collection_item_type =
        typename std::conditional<
        std::is_reference<iterator_deref<Container>>::value,
        std::reference_wrapper<
            typename std::remove_reference<
            iterator_deref<Container>>::type>,
        typename std::remove_const<
            iterator_deref<Container>>::type>::type;

    template <typename... Ts>
    struct are_same : std::true_type { };

    template <typename T, typename U, typename... Ts>
    struct are_same<T, U, Ts...>
        : std::integral_constant<bool,
            std::is_same<T, U>::value && are_same<T, Ts...>::value> { };
}

#endif // #ifndef ITERBASE_HPP_
<|endoftext|>


<commit_before>/*
 * REmbeddedWin32.cpp
 *
 * Copyright (C) 2009-11 by RStudio, Inc.
 *
 * This program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include <windows.h>
#undef TRUE
#undef FALSE

#define R_INTERNAL_FUNCTIONS
#include <r/RInternal.hpp>

#define Win32
#include "REmbedded.hpp"

#include <stdio.h>

#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time_duration.hpp>

#include <core/FilePath.hpp>
#include <core/Exec.hpp>

#include <r/RInterface.hpp>
#include <r/RFunctionHook.hpp>
#include <r/RExec.hpp>
#include <r/session/REventLoop.hpp>

#include <Rembedded.h>
#include <graphapp.h>

// from Defn.h
extern "C" void R_ProcessEvents(void);

// from Startup.h
extern "C" void R_CleanUp(SA_TYPE, int, int);

// from system.c
extern "C" UImode CharacterMode;

// for do_edit fork
extern "C" FILE *R_fopen(const char *filename, const char *mode);
extern "C" void R_ResetConsole(void);
#define FORSOURCING		95 /* not DELAYPROMISES, used in edit.c */
extern "C" SEXP Rf_deparse1(SEXP,Rboolean,int);

using namespace core;

namespace r {
namespace session {

namespace {

void (*s_polledEventHandler)(void) = NULL;
void rPolledEventCallback()
{
   if (s_polledEventHandler != NULL)
      s_polledEventHandler();
}

void showMessage(const char *info)
{
   ::MessageBoxA(::GetForegroundWindow(),
                 info,
                 "R Message",
                 MB_ICONEXCLAMATION | MB_OK);
}

#define YES    1
#define NO    -1
#define CANCEL 0
int askYesNoCancel(const char* question)
{
   if (!question)
      question = "";

   int result = ::MessageBoxA(::GetForegroundWindow(),
                              question,
                              "Question",
                              MB_ICONQUESTION | MB_YESNOCANCEL);

   switch(result)
   {
   case IDYES:
      return YES;
   case IDNO:
      return NO;
   case IDCANCEL:
      return CANCEL;
   }
}

// save callbacks for delegation
Callbacks s_callbacks;

SEXP loadHistoryHook(SEXP call, SEXP op, SEXP args, SEXP rho)
{
   r::function_hook::checkArity(op, args, call);
   s_callbacks.loadhistory(call, op, args, rho);
   return R_NilValue;
}

SEXP saveHistoryHook(SEXP call, SEXP op, SEXP args, SEXP rho)
{
   r::function_hook::checkArity(op, args, call);
   s_callbacks.savehistory(call, op, args, rho);
   return R_NilValue;
}

SEXP addHistoryHook(SEXP call, SEXP op, SEXP args, SEXP rho)
{
   r::function_hook::checkArity(op, args, call);
   s_callbacks.addhistory(call, op, args, rho);
   return R_NilValue;
}

// defines to allow us to make minimal mods to forked do_fileshow & do_edit
extern "C" void Rf_checkArityCall(SEXP op, SEXP args, SEXP call);
#define checkArity(a,b) Rf_checkArityCall(a,b,call)
#define _(String) String

// for Win32 we had to fork this from platform.c (because Win32 doesn't
// make a hook available for R_ShowFiles). The only change made to the
// function is to substitute s_callbacks.showFiles for R_ShowFiles
SEXP fileShowHook(SEXP call, SEXP op, SEXP args, SEXP rho)
{
    SEXP fn, tl, hd, pg;
    const char **f, **h, *t, *pager = NULL /* -Wall */;
    Rboolean dl;
    int i, n;

    checkArity(op, args);
    fn = CAR(args); args = CDR(args);
    hd = CAR(args); args = CDR(args);
    tl = CAR(args); args = CDR(args);
    dl = (Rboolean) Rf_asLogical(CAR(args)); args = CDR(args);
    pg = CAR(args);
    n = 0;			/* -Wall */
    if (!Rf_isString(fn) || (n = Rf_length(fn)) < 1)
   Rf_error(_("invalid filename specification"));
    if (!Rf_isString(hd) || Rf_length(hd) != n)
   Rf_error(_("invalid '%s' argument"), "headers");
    if (!Rf_isString(tl))
   Rf_error(_("invalid '%s' argument"), "title");
    if (!Rf_isString(pg))
   Rf_error(_("invalid '%s' argument"), "pager");
    f = (const char**) R_alloc(n, sizeof(char*));
    h = (const char**) R_alloc(n, sizeof(char*));
    for (i = 0; i < n; i++) {
   SEXP el = STRING_ELT(fn, i);
   if (!Rf_isNull(el) && el != NA_STRING)
#ifdef Win32
       f[i] = Rf_acopy_string(Rf_reEnc(CHAR(el), Rf_getCharCE(el), CE_UTF8, 1));
#else
       f[i] = Rf_acopy_string(Rf_translateChar(el));
#endif
   else
            Rf_error(_("invalid filename specification"));
   if (STRING_ELT(hd, i) != NA_STRING)
       h[i] = Rf_acopy_string(Rf_translateChar(STRING_ELT(hd, i)));
   else
            Rf_error(_("invalid '%s' argument"), "headers");
    }
    if (Rf_isValidStringF(tl))
   t = Rf_acopy_string(Rf_translateChar(STRING_ELT(tl, 0)));
    else
   t = "";
    if (Rf_isValidStringF(pg)) {
   SEXP pg0 = STRING_ELT(pg, 0);
        if (pg0 != NA_STRING)
            pager = Rf_acopy_string(CHAR(pg0));
        else
            Rf_error(_("invalid '%s' argument"), "pager");
    } else
   pager = "";
    s_callbacks.showFiles(n, f, h, t, dl, pager);
    return R_NilValue;
}

// for Win32 we had to fork this from edit.c (because Win32 R doesn't
// make a hook avaialabe for R_EditFile).
static char *DefaultFileName = NULL;
static int  EdFileUsed = 0;
SEXP editHook(SEXP call, SEXP op, SEXP args, SEXP rho)
{
    int   i;
    SEXP  x, fn, envir, src, srcfile, Rfn;
    char *filename;
    const void *vmaxsave;
    FILE *fp;

   checkArity(op, args);

    vmaxsave = vmaxget();

    x = CAR(args); args = CDR(args);
    if (TYPEOF(x) == CLOSXP) envir = CLOENV(x);
    else envir = R_NilValue;
    PROTECT(envir);

    fn = CAR(args); args = CDR(args);
    if (!Rf_isString(fn))
   Rf_error(_("invalid argument to edit()"));

    if (LENGTH(STRING_ELT(fn, 0)) > 0) {
   const char *ss = Rf_translateChar(STRING_ELT(fn, 0));
   filename = R_alloc(strlen(ss), sizeof(char));
   strcpy(filename, ss);
    }
    else filename = DefaultFileName;

    srcfile = R_NilValue;
    if (x != R_NilValue) {

   if((fp=R_fopen(R_ExpandFileName(filename), "w")) == NULL)
       Rf_errorcall(call, _("unable to open file"));
   if (LENGTH(STRING_ELT(fn, 0)) == 0) EdFileUsed++;
   if (TYPEOF(x) != CLOSXP || Rf_isNull(src = Rf_getAttrib(x, R_SourceSymbol)))
       src = Rf_deparse1(x, FALSE, FORSOURCING); /* deparse for sourcing, not for display */
   for (i = 0; i < LENGTH(src); i++)
       fprintf(fp, "%s\n", Rf_translateChar(STRING_ELT(src, i)));
   fclose(fp);
   PROTECT(Rfn = Rf_findFun(Rf_install("srcfilecopy"), R_BaseEnv));
   PROTECT(srcfile = Rf_lang3(Rfn, Rf_ScalarString(Rf_mkChar("<tmp>")), src));
   PROTECT(srcfile = Rf_eval(srcfile, R_BaseEnv));
   UNPROTECT(3);
    }
    PROTECT(srcfile);

    // edit the file
    s_callbacks.editFile(filename);

    if (!Rf_isNull(srcfile)) {
   PROTECT(Rfn = Rf_findFun(Rf_install("readLines"), R_BaseEnv));
   PROTECT(src = Rf_lang2(Rfn, Rf_ScalarString(Rf_mkChar(R_ExpandFileName(filename)))));
   PROTECT(src =Rf_eval(src, R_BaseEnv));
   Rf_defineVar(Rf_install("lines"), src, srcfile);
   UNPROTECT(3);
    }

   // parse the file by calling parse directly (because we can't access
   // R_ParseFile and related internals)
    r::sexp::Protect rProtect;
    Error error;
    {
       r::exec::RFunction parseFn("parse");
       parseFn.addParam(std::string(R_ExpandFileName(filename)));
       error = parseFn.call(&x, &rProtect);
    }
    if (error)
      Rf_errorcall(call, "expression parsing error");

    R_ResetConsole();
    {   /* can't just eval(x) here */
   int j, n;
   SEXP tmp = R_NilValue;

   n = LENGTH(x);
   for (j = 0 ; j < n ; j++)
       tmp = Rf_eval(VECTOR_ELT(x, j), R_GlobalEnv);
   x = tmp;
    }
    if (TYPEOF(x) == CLOSXP && envir != R_NilValue)
   SET_CLOENV(x, envir);
    UNPROTECT(2);
    vmaxset(vmaxsave);
    return (x);
}

void setMemoryLimit()
{
   // set defaults for R_max_memory. this code is based on similar code
   // in cmdlineoptions in system.c (but calls memory.limit directly rather
   // than setting R_max_memory directly, which we can't do because it
   // isn't exported from the R.dll
   const DWORDLONG Mega = 1048576;
   MEMORYSTATUSEX ms;
   ms.dwLength = sizeof(MEMORYSTATUSEX);
   ::GlobalMemoryStatusEx(&ms);
   DWORDLONG virtualMem = ms.ullTotalVirtual;
   DWORDLONG physicalMem = ms.ullTotalPhys;

 #ifdef WIN64
   DWORDLONG maxMemory = physicalMem;
 #else
   DWORDLONG maxMemory = std::min(virtualMem - 512*Mega, physicalMem);
 #endif
   // need enough to start R, with some head room
   maxMemory = std::max(32 * Mega, maxMemory);

   // call the memory.limit function
   maxMemory = maxMemory / Mega;
   r::exec::RFunction memoryLimit(".rs.setMemoryLimit");
   memoryLimit.addParam((double)maxMemory);
   Error error = memoryLimit.call();
   if (error)
      LOG_ERROR(error);
}

}

void runEmbeddedR(const core::FilePath& rHome,
                  const core::FilePath& userHome,
                  bool newSession,
                  SA_TYPE defaultSaveAction,
                  const Callbacks& callbacks,
                  InternalCallbacks* pInternal)
{
   // save callbacks for delegation
   s_callbacks = callbacks;

   // no signal handlers (see comment in REmbeddedPosix.cpp for rationale)
   R_SignalHandlers = 0;

   // set start time
   ::R_setStartTime();

   // setup params structure
   structRstart rp;
   Rstart pRP = &rp;
   ::R_DefParams(pRP);

   // set paths (copy to new string so we can provide char*)
   std::string* pRHome = new std::string(rHome.absolutePath());
   std::string* pUserHome = new std::string(userHome.absolutePath());
   pRP->rhome = const_cast<char*>(pRHome->c_str());
   pRP->home = const_cast<char*>(pUserHome->c_str());

   // more configuration
   pRP->CharacterMode = RGui;
   pRP->R_Slave = FALSE;
   pRP->R_Quiet = newSession ? FALSE : TRUE;
   pRP->R_Interactive = TRUE;
   pRP->SaveAction = defaultSaveAction;
   pRP->RestoreAction = SA_NORESTORE;
   pRP->LoadInitFile = newSession ? TRUE : FALSE;

   // hooks
   pRP->ReadConsole = callbacks.readConsole;
   pRP->WriteConsole = NULL;
   pRP->WriteConsoleEx = callbacks.writeConsoleEx;
   pRP->CallBack = rPolledEventCallback;
   pRP->ShowMessage = showMessage;
   pRP->YesNoCancel = askYesNoCancel;
   pRP->Busy = callbacks.busy;

   // set internal callbacks
   pInternal->cleanUp = R_CleanUp;
   pInternal->suicide = R_Suicide;

   // set command line
   const char *args[]= {"RStudio", "--interactive"};
   int argc = sizeof(args)/sizeof(args[0]);
   ::R_set_command_line_arguments(argc, (char**)args);

   // set params
   ::R_SetParams(pRP);

   // clear console input buffer
   ::FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE));

   // R global ui initialization
   ::GA_initapp(0, 0);
   ::readconsolecfg();

   // Set CharacterMode to LinkDLL during main loop setup. The mode can't be
   // RGui during setup_Rmainloop or calls to history functions (e.g. timestamp)
   // which occur during .Rprofile execution will crash when R attempts to
   // interact with the (non-existent) R gui console data structures. Note that
   // there are no references to CharacterMode within setup_Rmainloop that we
   // can see so the only side effect of this should be the disabling of the
   // console history mechanism.
   CharacterMode = LinkDLL;

   // setup main loop
   ::setup_Rmainloop();

   // reset character mode to RGui
   CharacterMode = RGui;

   // run main loop
   ::run_Rmainloop();
}

Error completeEmbeddedRInitialization()
{
   // set memory limit
   setMemoryLimit();

   // use IE proxy settings
   Error error = r::exec::executeString("suppressWarnings(utils::setInternet2())");
   if (error)
      LOG_ERROR(error);

   // from InitEd in edit.c
   EdFileUsed = 0;
   DefaultFileName = R_tmpnam("Redit", R_TempDir);

   using boost::bind;
   using namespace r::function_hook ;
   ExecBlock block ;
   block.addFunctions()
      (bind(registerReplaceHook, "loadhistory", loadHistoryHook, (CCODE*)NULL))
      (bind(registerReplaceHook, "savehistory", saveHistoryHook,(CCODE*)NULL))
      (bind(registerReplaceHook, "addhistory", addHistoryHook,(CCODE*)NULL))
      (bind(registerReplaceHook, "file.show", fileShowHook, (CCODE*)NULL))
      (bind(registerReplaceHook, "edit", editHook, (CCODE*)NULL))
      (bind(registerUnsupported, "winMenuAdd", "utils"))
      (bind(registerUnsupported, "winMenuAddItem", "utils"))
      (bind(registerUnsupported, "winMenuDel", "utils"))
      (bind(registerUnsupported, "winMenuDelItem", "utils"))
      (bind(registerUnsupported, "winMenuNames", "utils"))
      (bind(registerUnsupported, "winMenuItems", "utils"));
   return block.execute();
}

namespace event_loop {

void initializePolledEventHandler(void (*newPolledEventHandler)(void))
{
   s_polledEventHandler = newPolledEventHandler;
}

void permanentlyDisablePolledEventHandler()
{
   s_polledEventHandler = NULL;
}

bool polledEventHandlerInitialized()
{
   return s_polledEventHandler != NULL;
}

void processEvents()
{
   R_ProcessEvents();
}

} // namespace event_loop
} // namespace session
} // namespace r

<commit_msg>Revert "set char mode to LinkDLL during setup_Rmainloop to avoid interactions with win32 R history"<commit_after>/*
 * REmbeddedWin32.cpp
 *
 * Copyright (C) 2009-11 by RStudio, Inc.
 *
 * This program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include <windows.h>
#undef TRUE
#undef FALSE

#define R_INTERNAL_FUNCTIONS
#include <r/RInternal.hpp>

#define Win32
#include "REmbedded.hpp"

#include <stdio.h>

#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time_duration.hpp>

#include <core/FilePath.hpp>
#include <core/Exec.hpp>

#include <r/RInterface.hpp>
#include <r/RFunctionHook.hpp>
#include <r/RExec.hpp>
#include <r/session/REventLoop.hpp>

#include <Rembedded.h>
#include <graphapp.h>

// from Defn.h
extern "C" void R_ProcessEvents(void);

// from Startup.h
extern "C" void R_CleanUp(SA_TYPE, int, int);

// for do_edit fork
extern "C" FILE *R_fopen(const char *filename, const char *mode);
extern "C" void R_ResetConsole(void);
#define FORSOURCING		95 /* not DELAYPROMISES, used in edit.c */
extern "C" SEXP Rf_deparse1(SEXP,Rboolean,int);

using namespace core;

namespace r {
namespace session {

namespace {

void (*s_polledEventHandler)(void) = NULL;
void rPolledEventCallback()
{
   if (s_polledEventHandler != NULL)
      s_polledEventHandler();
}

void showMessage(const char *info)
{
   ::MessageBoxA(::GetForegroundWindow(),
                 info,
                 "R Message",
                 MB_ICONEXCLAMATION | MB_OK);
}

#define YES    1
#define NO    -1
#define CANCEL 0
int askYesNoCancel(const char* question)
{
   if (!question)
      question = "";

   int result = ::MessageBoxA(::GetForegroundWindow(),
                              question,
                              "Question",
                              MB_ICONQUESTION | MB_YESNOCANCEL);

   switch(result)
   {
   case IDYES:
      return YES;
   case IDNO:
      return NO;
   case IDCANCEL:
      return CANCEL;
   }
}

// save callbacks for delegation
Callbacks s_callbacks;

SEXP loadHistoryHook(SEXP call, SEXP op, SEXP args, SEXP rho)
{
   r::function_hook::checkArity(op, args, call);
   s_callbacks.loadhistory(call, op, args, rho);
   return R_NilValue;
}

SEXP saveHistoryHook(SEXP call, SEXP op, SEXP args, SEXP rho)
{
   r::function_hook::checkArity(op, args, call);
   s_callbacks.savehistory(call, op, args, rho);
   return R_NilValue;
}

SEXP addHistoryHook(SEXP call, SEXP op, SEXP args, SEXP rho)
{
   r::function_hook::checkArity(op, args, call);
   s_callbacks.addhistory(call, op, args, rho);
   return R_NilValue;
}

// defines to allow us to make minimal mods to forked do_fileshow & do_edit
extern "C" void Rf_checkArityCall(SEXP op, SEXP args, SEXP call);
#define checkArity(a,b) Rf_checkArityCall(a,b,call)
#define _(String) String

// for Win32 we had to fork this from platform.c (because Win32 doesn't
// make a hook available for R_ShowFiles). The only change made to the
// function is to substitute s_callbacks.showFiles for R_ShowFiles
SEXP fileShowHook(SEXP call, SEXP op, SEXP args, SEXP rho)
{
    SEXP fn, tl, hd, pg;
    const char **f, **h, *t, *pager = NULL /* -Wall */;
    Rboolean dl;
    int i, n;

    checkArity(op, args);
    fn = CAR(args); args = CDR(args);
    hd = CAR(args); args = CDR(args);
    tl = CAR(args); args = CDR(args);
    dl = (Rboolean) Rf_asLogical(CAR(args)); args = CDR(args);
    pg = CAR(args);
    n = 0;			/* -Wall */
    if (!Rf_isString(fn) || (n = Rf_length(fn)) < 1)
   Rf_error(_("invalid filename specification"));
    if (!Rf_isString(hd) || Rf_length(hd) != n)
   Rf_error(_("invalid '%s' argument"), "headers");
    if (!Rf_isString(tl))
   Rf_error(_("invalid '%s' argument"), "title");
    if (!Rf_isString(pg))
   Rf_error(_("invalid '%s' argument"), "pager");
    f = (const char**) R_alloc(n, sizeof(char*));
    h = (const char**) R_alloc(n, sizeof(char*));
    for (i = 0; i < n; i++) {
   SEXP el = STRING_ELT(fn, i);
   if (!Rf_isNull(el) && el != NA_STRING)
#ifdef Win32
       f[i] = Rf_acopy_string(Rf_reEnc(CHAR(el), Rf_getCharCE(el), CE_UTF8, 1));
#else
       f[i] = Rf_acopy_string(Rf_translateChar(el));
#endif
   else
            Rf_error(_("invalid filename specification"));
   if (STRING_ELT(hd, i) != NA_STRING)
       h[i] = Rf_acopy_string(Rf_translateChar(STRING_ELT(hd, i)));
   else
            Rf_error(_("invalid '%s' argument"), "headers");
    }
    if (Rf_isValidStringF(tl))
   t = Rf_acopy_string(Rf_translateChar(STRING_ELT(tl, 0)));
    else
   t = "";
    if (Rf_isValidStringF(pg)) {
   SEXP pg0 = STRING_ELT(pg, 0);
        if (pg0 != NA_STRING)
            pager = Rf_acopy_string(CHAR(pg0));
        else
            Rf_error(_("invalid '%s' argument"), "pager");
    } else
   pager = "";
    s_callbacks.showFiles(n, f, h, t, dl, pager);
    return R_NilValue;
}

// for Win32 we had to fork this from edit.c (because Win32 R doesn't
// make a hook avaialabe for R_EditFile).
static char *DefaultFileName = NULL;
static int  EdFileUsed = 0;
SEXP editHook(SEXP call, SEXP op, SEXP args, SEXP rho)
{
    int   i;
    SEXP  x, fn, envir, src, srcfile, Rfn;
    char *filename;
    const void *vmaxsave;
    FILE *fp;

   checkArity(op, args);

    vmaxsave = vmaxget();

    x = CAR(args); args = CDR(args);
    if (TYPEOF(x) == CLOSXP) envir = CLOENV(x);
    else envir = R_NilValue;
    PROTECT(envir);

    fn = CAR(args); args = CDR(args);
    if (!Rf_isString(fn))
   Rf_error(_("invalid argument to edit()"));

    if (LENGTH(STRING_ELT(fn, 0)) > 0) {
   const char *ss = Rf_translateChar(STRING_ELT(fn, 0));
   filename = R_alloc(strlen(ss), sizeof(char));
   strcpy(filename, ss);
    }
    else filename = DefaultFileName;

    srcfile = R_NilValue;
    if (x != R_NilValue) {

   if((fp=R_fopen(R_ExpandFileName(filename), "w")) == NULL)
       Rf_errorcall(call, _("unable to open file"));
   if (LENGTH(STRING_ELT(fn, 0)) == 0) EdFileUsed++;
   if (TYPEOF(x) != CLOSXP || Rf_isNull(src = Rf_getAttrib(x, R_SourceSymbol)))
       src = Rf_deparse1(x, FALSE, FORSOURCING); /* deparse for sourcing, not for display */
   for (i = 0; i < LENGTH(src); i++)
       fprintf(fp, "%s\n", Rf_translateChar(STRING_ELT(src, i)));
   fclose(fp);
   PROTECT(Rfn = Rf_findFun(Rf_install("srcfilecopy"), R_BaseEnv));
   PROTECT(srcfile = Rf_lang3(Rfn, Rf_ScalarString(Rf_mkChar("<tmp>")), src));
   PROTECT(srcfile = Rf_eval(srcfile, R_BaseEnv));
   UNPROTECT(3);
    }
    PROTECT(srcfile);

    // edit the file
    s_callbacks.editFile(filename);

    if (!Rf_isNull(srcfile)) {
   PROTECT(Rfn = Rf_findFun(Rf_install("readLines"), R_BaseEnv));
   PROTECT(src = Rf_lang2(Rfn, Rf_ScalarString(Rf_mkChar(R_ExpandFileName(filename)))));
   PROTECT(src =Rf_eval(src, R_BaseEnv));
   Rf_defineVar(Rf_install("lines"), src, srcfile);
   UNPROTECT(3);
    }

   // parse the file by calling parse directly (because we can't access
   // R_ParseFile and related internals)
    r::sexp::Protect rProtect;
    Error error;
    {
       r::exec::RFunction parseFn("parse");
       parseFn.addParam(std::string(R_ExpandFileName(filename)));
       error = parseFn.call(&x, &rProtect);
    }
    if (error)
      Rf_errorcall(call, "expression parsing error");

    R_ResetConsole();
    {   /* can't just eval(x) here */
   int j, n;
   SEXP tmp = R_NilValue;

   n = LENGTH(x);
   for (j = 0 ; j < n ; j++)
       tmp = Rf_eval(VECTOR_ELT(x, j), R_GlobalEnv);
   x = tmp;
    }
    if (TYPEOF(x) == CLOSXP && envir != R_NilValue)
   SET_CLOENV(x, envir);
    UNPROTECT(2);
    vmaxset(vmaxsave);
    return (x);
}

void setMemoryLimit()
{
   // set defaults for R_max_memory. this code is based on similar code
   // in cmdlineoptions in system.c (but calls memory.limit directly rather
   // than setting R_max_memory directly, which we can't do because it
   // isn't exported from the R.dll
   const DWORDLONG Mega = 1048576;
   MEMORYSTATUSEX ms;
   ms.dwLength = sizeof(MEMORYSTATUSEX);
   ::GlobalMemoryStatusEx(&ms);
   DWORDLONG virtualMem = ms.ullTotalVirtual;
   DWORDLONG physicalMem = ms.ullTotalPhys;

 #ifdef WIN64
   DWORDLONG maxMemory = physicalMem;
 #else
   DWORDLONG maxMemory = std::min(virtualMem - 512*Mega, physicalMem);
 #endif
   // need enough to start R, with some head room
   maxMemory = std::max(32 * Mega, maxMemory);

   // call the memory.limit function
   maxMemory = maxMemory / Mega;
   r::exec::RFunction memoryLimit(".rs.setMemoryLimit");
   memoryLimit.addParam((double)maxMemory);
   Error error = memoryLimit.call();
   if (error)
      LOG_ERROR(error);
}

}

void runEmbeddedR(const core::FilePath& rHome,
                  const core::FilePath& userHome,
                  bool newSession,
                  SA_TYPE defaultSaveAction,
                  const Callbacks& callbacks,
                  InternalCallbacks* pInternal)
{
   // save callbacks for delegation
   s_callbacks = callbacks;

   // no signal handlers (see comment in REmbeddedPosix.cpp for rationale)
   R_SignalHandlers = 0;

   // set start time
   ::R_setStartTime();

   // setup params structure
   structRstart rp;
   Rstart pRP = &rp;
   ::R_DefParams(pRP);

   // set paths (copy to new string so we can provide char*)
   std::string* pRHome = new std::string(rHome.absolutePath());
   std::string* pUserHome = new std::string(userHome.absolutePath());
   pRP->rhome = const_cast<char*>(pRHome->c_str());
   pRP->home = const_cast<char*>(pUserHome->c_str());

   // more configuration
   pRP->CharacterMode = RGui;
   pRP->R_Slave = FALSE;
   pRP->R_Quiet = newSession ? FALSE : TRUE;
   pRP->R_Interactive = TRUE;
   pRP->SaveAction = defaultSaveAction;
   pRP->RestoreAction = SA_NORESTORE;
   pRP->LoadInitFile = newSession ? TRUE : FALSE;

   // hooks
   pRP->ReadConsole = callbacks.readConsole;
   pRP->WriteConsole = NULL;
   pRP->WriteConsoleEx = callbacks.writeConsoleEx;
   pRP->CallBack = rPolledEventCallback;
   pRP->ShowMessage = showMessage;
   pRP->YesNoCancel = askYesNoCancel;
   pRP->Busy = callbacks.busy;

   // set internal callbacks
   pInternal->cleanUp = R_CleanUp;
   pInternal->suicide = R_Suicide;

   // set command line
   const char *args[]= {"RStudio", "--interactive"};
   int argc = sizeof(args)/sizeof(args[0]);
   ::R_set_command_line_arguments(argc, (char**)args);

   // set params
   ::R_SetParams(pRP);

   // clear console input buffer
   ::FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE));

   // R global ui initialization
   ::GA_initapp(0, 0);
   ::readconsolecfg();

   // run main loop
   ::setup_Rmainloop();
   ::run_Rmainloop();
}

Error completeEmbeddedRInitialization()
{
   // set memory limit
   setMemoryLimit();

   // use IE proxy settings
   Error error = r::exec::executeString("suppressWarnings(utils::setInternet2())");
   if (error)
      LOG_ERROR(error);

   // from InitEd in edit.c
   EdFileUsed = 0;
   DefaultFileName = R_tmpnam("Redit", R_TempDir);

   using boost::bind;
   using namespace r::function_hook ;
   ExecBlock block ;
   block.addFunctions()
      (bind(registerReplaceHook, "loadhistory", loadHistoryHook, (CCODE*)NULL))
      (bind(registerReplaceHook, "savehistory", saveHistoryHook,(CCODE*)NULL))
      (bind(registerReplaceHook, "addhistory", addHistoryHook,(CCODE*)NULL))
      (bind(registerReplaceHook, "file.show", fileShowHook, (CCODE*)NULL))
      (bind(registerReplaceHook, "edit", editHook, (CCODE*)NULL))
      (bind(registerUnsupported, "winMenuAdd", "utils"))
      (bind(registerUnsupported, "winMenuAddItem", "utils"))
      (bind(registerUnsupported, "winMenuDel", "utils"))
      (bind(registerUnsupported, "winMenuDelItem", "utils"))
      (bind(registerUnsupported, "winMenuNames", "utils"))
      (bind(registerUnsupported, "winMenuItems", "utils"));
   return block.execute();
}

namespace event_loop {

void initializePolledEventHandler(void (*newPolledEventHandler)(void))
{
   s_polledEventHandler = newPolledEventHandler;
}

void permanentlyDisablePolledEventHandler()
{
   s_polledEventHandler = NULL;
}

bool polledEventHandlerInitialized()
{
   return s_polledEventHandler != NULL;
}

void processEvents()
{
   R_ProcessEvents();
}

} // namespace event_loop
} // namespace session
} // namespace r

<|endoftext|>


<commit_before>// Copyright (c) 2014 Sze Howe Koh
// This code is licensed under the MIT license (see LICENSE.MIT for details)

#include "downloader.h"
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QDebug>
#include <QDir>

Downloader::Downloader(QObject* parent)
	: QObject(parent)
	, nam(new QNetworkAccessManager(this))
{}

void
Downloader::loadMirrors()
{
	qDebug() << "Downloading mirror list...";

	// ASSUMPTION: All mirrors host packages for all architectures
	// ASSUMPTION: Updates.xml is the core file
	QNetworkRequest req(QUrl("http://download.qt-project.org/online/qtsdkrepository/windows_x86/root/qt/Updates.xml.mirrorlist"));
	auto reply = nam->get(req);
	connect(reply, &QNetworkReply::finished, [=]
	{
		QString page = reply->readAll();
		reply->deleteLater();
		if (reply->error() != QNetworkReply::NoError)
			return;

		QStringList tmp = page.split("mirrors in other parts of the world");
		if (tmp.size() < 2)
		{
			qDebug() << "\tERROR: Malformed file:\n" << page;
			return;
		}

		// ASSUMPTION: After the heading "Found N mirrors in other parts of the world", only
		//             the mirrored file URLs are in a list
		QStringList mirrors = tmp[1].split("
<a href=\"");
		mirrors.removeFirst();

		for (int i = 0; i < mirrors.size(); ++i)
		{
			QString rootUrl = mirrors[i].split("online/qtsdkrepository").first();

			// Replace list with domains only
			QString& domain = mirrors[i];
			domain = rootUrl;
			domain.remove("http://");
			domain = domain.split('/').first();

			mirrorMap[domain] = rootUrl;
		}

		qDebug() << "\tDone";

		// The original order, with the "best" mirror on top, is preserved
		emit mirrorsFound(mirrors);

		// ASSUMPTION: There will be at least 1 mirror found
	});

	connect(reply, ( void (QNetworkReply::*)(QNetworkReply::NetworkError) )&QNetworkReply::error, [=]
	{
		qDebug() << "\tERROR:" << reply->errorString();
	});
}

//=============
// PUBLIC SLOTS

void
Downloader::useMirror(const QString& sdkArch, const QString& mirrorDomain)
{
	_sdkArch = sdkArch;
	_rootUrl = mirrorMap[mirrorDomain];

	QString subdir = "online/qtsdkrepository/" + sdkArch + "/root/qt/";

	// Delete old files
	QDir place(subdir + "qt/");
	place.removeRecursively();
	place.mkpath(".");

	initCounters();

	qDebug() << "Downloading raw metadata...";
	getFilesFromIndex(_rootUrl, subdir,
			{".xml"});
	getFilesFromIndex(_rootUrl, subdir + "qt/",
			{".7z", ".sha1"});
}

// =======
// PRIVATE

void
Downloader::initCounters()
{
	expectedFileCount = 0;
	loadedFileCount = 0;
	expectedIndexCount = 0;
	loadedIndexCount = 0;
}

static bool
isWanted(const QString& link, const QStringList& wantedExtensions)
{
	for (const QString& ext : wantedExtensions)
	{
		if (link.endsWith(ext))
			return true;
	}
	return false;
}

void
Downloader::getFilesFromIndex(const QString& rootUrl, const QString& relPath, const QStringList& wantedExtensions)
{
	// ASSUMPTION: relPath ends with '/'

	++expectedIndexCount;
	QNetworkRequest indexReq(QUrl(rootUrl + relPath));
	QNetworkReply* indexReply = nam->get(indexReq);
	connect(indexReply, &QNetworkReply::finished, [=]
	{
		++loadedIndexCount;

		QString page = indexReply->readAll();
		indexReply->deleteLater();

		// ASSUMPTION: There's no whitespace around the '='
		QStringList links = page.split("a href=\"");
		links.removeFirst();

		QStringList wantedFiles;
		for (int i = 0; i < links.size(); ++i)
		{
			QString link = links[i].split('"').at(0);
			if (isWanted(link, wantedExtensions))
				wantedFiles << link;
		}

		expectedFileCount += wantedFiles.size();
		for (const QString& filename : wantedFiles)
		{
			QNetworkRequest fileReq(QUrl(rootUrl + relPath + filename));
			auto fileReply = nam->get(fileReq);
			connect(fileReply, &QNetworkReply::finished, [=]
			{
				qDebug() << "\tLoaded" << fileReply->url().toString();

				QByteArray data = fileReply->readAll();
				fileReply->deleteLater();

				QFile fileOut(relPath + filename);
				if (!fileOut.open(QFile::WriteOnly))
				{
					qDebug() << "\tERROR: Unable to open file for writing:" << fileOut.fileName();
					return;
				}

				fileOut.write(data);
				fileOut.close();

				this->recordDownload();
			});

			connect(fileReply, ( void (QNetworkReply::*)(QNetworkReply::NetworkError) )&QNetworkReply::error, [=]
			{
				qDebug() << "\tERROR:" << indexReply->errorString();
			});
		}
	});

	connect(indexReply, ( void (QNetworkReply::*)(QNetworkReply::NetworkError) )&QNetworkReply::error, [=]
	{
		qDebug() << "\tERROR:" << indexReply->errorString();
	});
}

void
Downloader::recordDownload()
{
	++loadedFileCount;

	if (loadedFileCount == expectedFileCount && loadedIndexCount == expectedIndexCount)
		emit metadataLoaded(_sdkArch, _rootUrl);
}
<commit_msg>FIX: Error was printed from the wrong QNetworkReply<commit_after>// Copyright (c) 2014 Sze Howe Koh
// This code is licensed under the MIT license (see LICENSE.MIT for details)

#include "downloader.h"
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QDebug>
#include <QDir>

Downloader::Downloader(QObject* parent)
	: QObject(parent)
	, nam(new QNetworkAccessManager(this))
{}

void
Downloader::loadMirrors()
{
	qDebug() << "Downloading mirror list...";

	// ASSUMPTION: All mirrors host packages for all architectures
	// ASSUMPTION: Updates.xml is the core file
	QNetworkRequest req(QUrl("http://download.qt-project.org/online/qtsdkrepository/windows_x86/root/qt/Updates.xml.mirrorlist"));
	auto reply = nam->get(req);
	connect(reply, &QNetworkReply::finished, [=]
	{
		QString page = reply->readAll();
		reply->deleteLater();
		if (reply->error() != QNetworkReply::NoError)
			return;

		QStringList tmp = page.split("mirrors in other parts of the world");
		if (tmp.size() < 2)
		{
			qDebug() << "\tERROR: Malformed file:\n" << page;
			return;
		}

		// ASSUMPTION: After the heading "Found N mirrors in other parts of the world", only
		//             the mirrored file URLs are in a list
		QStringList mirrors = tmp[1].split("
<a href=\"");
		mirrors.removeFirst();

		for (int i = 0; i < mirrors.size(); ++i)
		{
			QString rootUrl = mirrors[i].split("online/qtsdkrepository").first();

			// Replace list with domains only
			QString& domain = mirrors[i];
			domain = rootUrl;
			domain.remove("http://");
			domain = domain.split('/').first();

			mirrorMap[domain] = rootUrl;
		}

		qDebug() << "\tDone";

		// The original order, with the "best" mirror on top, is preserved
		emit mirrorsFound(mirrors);

		// ASSUMPTION: There will be at least 1 mirror found
	});

	connect(reply, ( void (QNetworkReply::*)(QNetworkReply::NetworkError) )&QNetworkReply::error, [=]
	{
		qDebug() << "\tERROR:" << reply->errorString();
	});
}

//=============
// PUBLIC SLOTS

void
Downloader::useMirror(const QString& sdkArch, const QString& mirrorDomain)
{
	_sdkArch = sdkArch;
	_rootUrl = mirrorMap[mirrorDomain];

	QString subdir = "online/qtsdkrepository/" + sdkArch + "/root/qt/";

	// Delete old files
	QDir place(subdir + "qt/");
	place.removeRecursively();
	place.mkpath(".");

	initCounters();

	qDebug() << "Downloading raw metadata...";
	getFilesFromIndex(_rootUrl, subdir,
			{".xml"});
	getFilesFromIndex(_rootUrl, subdir + "qt/",
			{".7z", ".sha1"});
}

// =======
// PRIVATE

void
Downloader::initCounters()
{
	expectedFileCount = 0;
	loadedFileCount = 0;
	expectedIndexCount = 0;
	loadedIndexCount = 0;
}

static bool
isWanted(const QString& link, const QStringList& wantedExtensions)
{
	for (const QString& ext : wantedExtensions)
	{
		if (link.endsWith(ext))
			return true;
	}
	return false;
}

void
Downloader::getFilesFromIndex(const QString& rootUrl, const QString& relPath, const QStringList& wantedExtensions)
{
	// ASSUMPTION: relPath ends with '/'

	++expectedIndexCount;
	QNetworkRequest indexReq(QUrl(rootUrl + relPath));
	QNetworkReply* indexReply = nam->get(indexReq);
	connect(indexReply, &QNetworkReply::finished, [=]
	{
		++loadedIndexCount;

		QString page = indexReply->readAll();
		indexReply->deleteLater();

		// ASSUMPTION: There's no whitespace around the '='
		QStringList links = page.split("a href=\"");
		links.removeFirst();

		QStringList wantedFiles;
		for (int i = 0; i < links.size(); ++i)
		{
			QString link = links[i].split('"').at(0);
			if (isWanted(link, wantedExtensions))
				wantedFiles << link;
		}

		expectedFileCount += wantedFiles.size();
		for (const QString& filename : wantedFiles)
		{
			QNetworkRequest fileReq(QUrl(rootUrl + relPath + filename));
			auto fileReply = nam->get(fileReq);
			connect(fileReply, &QNetworkReply::finished, [=]
			{
				qDebug() << "\tLoaded" << fileReply->url().toString();

				QByteArray data = fileReply->readAll();
				fileReply->deleteLater();

				QFile fileOut(relPath + filename);
				if (!fileOut.open(QFile::WriteOnly))
				{
					qDebug() << "\tERROR: Unable to open file for writing:" << fileOut.fileName();
					return;
				}

				fileOut.write(data);
				fileOut.close();

				this->recordDownload();
			});

			connect(fileReply, ( void (QNetworkReply::*)(QNetworkReply::NetworkError) )&QNetworkReply::error, [=]
			{
				qDebug() << "\tERROR:" << fileReply->errorString();
			});
		}
	});

	connect(indexReply, ( void (QNetworkReply::*)(QNetworkReply::NetworkError) )&QNetworkReply::error, [=]
	{
		qDebug() << "\tERROR:" << indexReply->errorString();
	});
}

void
Downloader::recordDownload()
{
	++loadedFileCount;

	if (loadedFileCount == expectedFileCount && loadedIndexCount == expectedIndexCount)
		emit metadataLoaded(_sdkArch, _rootUrl);
}
<|endoftext|>


<commit_before>#include <iostream>
#include <cmath>
#include <random>
#include <complex>
#include <typeinfo>

#include "gif.hpp"

using namespace std;

const int size = 256;

class Pixel
{
public:
    void red(uint8_t color){
        px &= 0xFFFFFF00;
        px |= static_cast<uint32_t>(color);
    }
    void green(uint8_t color){
        px &= 0xFFFF00FF;
        px |= static_cast<uint32_t>(color) << 8;
    }
    void blue(uint8_t color){
        px &= 0xFF00FFFF;
        px |= static_cast<uint32_t>(color) << 16;
    }
    Pixel& operator=(uint32_t color){
        px=color;
        return *this;
    }
private:
    uint32_t px{0};
};

class Frame
{
public:
    Pixel& point(int row, int col){return arr[row][col];}
    operator void*(){return arr;}
private:
    Pixel arr[size][size];
};

int main()
{
    //
    Frame frame;

    //   (gif-)
    GifWriter gw("checking.gif",size,size);

    //
    for(int i=0;i<size;i++)
        for(int j=0;j<size;j++)
        {
            frame.point(i,j).red( (i+j)/2 );    //
            frame.point(i,j).green( (i+j)/4 );  //
            frame.point(i,j).blue( (i+j)/3 );   //
        }
    gw.write(frame);

    //
    for(int i=0;i<size;i++)
        for(int j=0;j<size;j++)
            frame.point(i,j)=0x00AAFFAA; // 0x00BBGGRR -
    gw.write(frame);

    //
    for(int i=0;i<size;i++)
        frame.point( size/2+int(size/4*sin(0.1*i)), i) = 0;
    gw.write(frame);

    //
    std::default_random_engine generator;
    std::normal_distribution<double> distribution(0.0, 0.5); //     127   32
    for(int i=0;i<size;i++)
        for(int j=0;j<size;j++)
        {
            frame.point(i,j).red  ( distribution(generator) );    //
            frame.point(i,j).green( distribution(generator) );    //
            frame.point(i,j).blue ( distribution(generator) );    //
        }
    gw.write(frame);

    //   (http://elementy.ru/posters/fractals)
    complex<double> c={-0.2,0.7};
    for(int i=0;i<size;i++)
    {
        for(int j=0;j<size;j++)
        {
            frame.point(i,j) = 0;
            complex<double> x ={static_cast<double>(j)/size-0.5, static_cast<double>(i)/size-0.5};
            x *= 3;
            for(int n=0; n<20; n++)
            {
                x = x*x+c;
                if(std::abs(x) > 10.0 )
                {
                    frame.point(i,j) = n*255/20;
                    break;
                }
            }
        }
    }
    gw.write(frame);

    return 0;
}
<commit_msg>a function returning reference to 2-D array element replaced by operator[]<commit_after>#include <iostream>
#include <cmath>
#include <random>
#include <complex>
#include <typeinfo>

#include "gif.hpp"

using namespace std;

const int size = 256;

class Pixel
{
public:
    void red(uint8_t color){
        px &= 0xFFFFFF00;
        px |= static_cast<uint32_t>(color);
    }
    void green(uint8_t color){
        px &= 0xFFFF00FF;
        px |= static_cast<uint32_t>(color) << 8;
    }
    void blue(uint8_t color){
        px &= 0xFF00FFFF;
        px |= static_cast<uint32_t>(color) << 16;
    }
    Pixel& operator=(uint32_t color){
        px=color;
        return *this;
    }
private:
    uint32_t px{0};
};

class Frame
{
    using FrameRow = Pixel(&)[size];
public:
    FrameRow operator[](int idx){return arr[idx];}
    operator void*(){return arr;}
private:
    Pixel arr[size][size];
};

int main()
{
    //
    Frame frame;

    //   (gif-)
    GifWriter gw("checking.gif",size,size);

    //
    for(int i=0;i<size;i++)
        for(int j=0;j<size;j++)
        {
            frame[i][j].red( (i+j)/2 );    //
            frame[i][j].green( (i+j)/4 );  //
            frame[i][j].blue( (i+j)/3 );   //
        }
    gw.write(frame);

    //
    for(int i=0;i<size;i++)
        for(int j=0;j<size;j++)
            frame[i][j]=0x00AAFFAA; // 0x00BBGGRR -
    gw.write(frame);

    //
    for(int i=0;i<size;i++)
        frame[size/2+int(size/4*sin(0.1*i))][i] = 0;
    gw.write(frame);

    //
    std::default_random_engine generator;
    std::normal_distribution<double> distribution(0.0, 0.5); //     127   32
    for(int i=0;i<size;i++)
        for(int j=0;j<size;j++)
        {
            frame[i][j].red  ( distribution(generator) );    //
            frame[i][j].green( distribution(generator) );    //
            frame[i][j].blue ( distribution(generator) );    //
        }
    gw.write(frame);

    //   (http://elementy.ru/posters/fractals)
    complex<double> c={-0.2,0.7};
    for(int i=0;i<size;i++)
    {
        for(int j=0;j<size;j++)
        {
            frame[i][j] = 0;
            complex<double> x ={static_cast<double>(j)/size-0.5, static_cast<double>(i)/size-0.5};
            x *= 3;
            for(int n=0; n<20; n++)
            {
                x = x*x+c;
                if(std::abs(x) > 10.0 )
                {
                    frame[i][j] = n*255/20;
                    break;
                }
            }
        }
    }
    gw.write(frame);

    return 0;
}
<|endoftext|>