<commit_before>/*

 Copyright (c) 2018, Ford Motor Company
 All rights reserved.

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

 Redistributions of source code must retain the above copyright notice, this
 list of conditions and the following disclaimer.

 Redistributions in binary form must reproduce the above copyright notice,
 this list of conditions and the following
 disclaimer in the documentation and/or other materials provided with the
 distribution.

 Neither the name of the Ford Motor Company nor the names of its contributors
 may be used to endorse or promote products derived from this software
 without specific prior written permission.

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER 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 "vehicle_info_plugin/commands/mobile/on_vehicle_data_notification.h"

#include "application_manager/application_impl.h"
#include "application_manager/helpers/application_helper.h"
#include "application_manager/message_helper.h"
#include "interfaces/MOBILE_API.h"
#include "utils/macro.h"
#include "vehicle_info_plugin/vehicle_info_app_extension.h"

namespace vehicle_info_plugin {
using namespace application_manager;

namespace commands {

SDL_CREATE_LOG_VARIABLE("Commands")

OnVehicleDataNotification::OnVehicleDataNotification(
    const application_manager::commands::MessageSharedPtr& message,
    const VehicleInfoCommandParams& params)
    : CommandNotificationImpl(message,
                              params.application_manager_,
                              params.rpc_service_,
                              params.hmi_capabilities_,
                              params.policy_handler_)
    , custom_vehicle_data_manager_(params.custom_vehicle_data_manager_) {}

OnVehicleDataNotification::~OnVehicleDataNotification() {}

void OnVehicleDataNotification::Run() {
  SDL_LOG_AUTO_TRACE();

  std::vector<ApplicationSharedPtr> notify_apps;
  std::vector<ApplicationSharedPtr>::iterator notified_app_it =
      notify_apps.begin();
  std::vector<smart_objects::SmartObject> appSO;

  custom_vehicle_data_manager_.CreateMobileMessageParams(
      (*message_)[strings::msg_params]);

  const auto& param_names = (*message_)[strings::msg_params].enumerate();
  for (const auto& name : param_names) {
    SDL_LOG_DEBUG("vehicle_data name: " << name);
    auto vehicle_data_value = (*message_)[strings::msg_params][name].asInt();
    application_manager_.IviInfoUpdated(name, vehicle_data_value);

    auto subscribed_to_ivi_predicate = [&name](const ApplicationSharedPtr app) {
      DCHECK_OR_RETURN(app, false);
      auto& ext = VehicleInfoAppExtension::ExtractVIExtension(*app);
      return ext.isSubscribedToVehicleInfo(name);
    };

    const auto& applications = application_manager::FindAllApps(
        application_manager_.applications(), subscribed_to_ivi_predicate);
    for (const auto& app : applications) {
      if (!app) {
        SDL_LOG_ERROR("NULL pointer");
        continue;
      }
      notified_app_it = find(notify_apps.begin(), notify_apps.end(), app);
      if (notified_app_it == notify_apps.end()) {
        notify_apps.push_back(app);
        smart_objects::SmartObject msg_param =
            smart_objects::SmartObject(smart_objects::SmartType_Map);
        msg_param[name] = (*message_)[strings::msg_params][name];
        appSO.push_back(msg_param);
      } else {
        size_t idx = std::distance(notify_apps.begin(), notified_app_it);
        appSO[idx][name] = (*message_)[strings::msg_params][name];
      }
    }
  }

  for (size_t idx = 0; idx < notify_apps.size(); idx++) {
    CommandParametersPermissions params_permissions;
    application_manager_.CheckPolicyPermissions(
        notify_apps[idx],
        window_id(),
        MessageHelper::StringifiedFunctionID(
            mobile_api::FunctionID::OnVehicleDataID),
        appSO[idx].enumerate(),
        &params_permissions);

    for (const auto& param : appSO[idx].enumerate()) {
      const auto& allowed_params = params_permissions.allowed_params;
      auto param_allowed = allowed_params.find(param);
      if (allowed_params.end() == param_allowed) {
        SDL_LOG_DEBUG("Param " << param << " is not allowed by policy for app "
                               << notify_apps[idx]->app_id()
                               << ". It will be ignored.");
        appSO[idx].erase(param);
      }
    }

    if (appSO[idx].empty()) {
      SDL_LOG_DEBUG("App " << notify_apps[idx]->app_id()
                           << " will be skipped: there is nothing to notify.");
      continue;
    }

    SDL_LOG_INFO("Send OnVehicleData notification to "
                 << notify_apps[idx]->name().c_str() << " application id "
                 << notify_apps[idx]->app_id());
    (*message_)[strings::params][strings::connection_key] =
        notify_apps[idx]->app_id();
    (*message_)[strings::msg_params] = appSO[idx];
    SendNotification();
  }
}

}  // namespace commands
}  // namespace vehicle_info_plugin
<commit_msg>Handle omitted parameter permissions properly in OnVehicleData notification<commit_after>/*

 Copyright (c) 2018, Ford Motor Company
 All rights reserved.

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

 Redistributions of source code must retain the above copyright notice, this
 list of conditions and the following disclaimer.

 Redistributions in binary form must reproduce the above copyright notice,
 this list of conditions and the following
 disclaimer in the documentation and/or other materials provided with the
 distribution.

 Neither the name of the Ford Motor Company nor the names of its contributors
 may be used to endorse or promote products derived from this software
 without specific prior written permission.

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER 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 "vehicle_info_plugin/commands/mobile/on_vehicle_data_notification.h"

#include "application_manager/application_impl.h"
#include "application_manager/helpers/application_helper.h"
#include "application_manager/message_helper.h"
#include "interfaces/MOBILE_API.h"
#include "utils/macro.h"
#include "vehicle_info_plugin/vehicle_info_app_extension.h"

namespace vehicle_info_plugin {
using namespace application_manager;

namespace commands {

SDL_CREATE_LOG_VARIABLE("Commands")

OnVehicleDataNotification::OnVehicleDataNotification(
    const application_manager::commands::MessageSharedPtr& message,
    const VehicleInfoCommandParams& params)
    : CommandNotificationImpl(message,
                              params.application_manager_,
                              params.rpc_service_,
                              params.hmi_capabilities_,
                              params.policy_handler_)
    , custom_vehicle_data_manager_(params.custom_vehicle_data_manager_) {}

OnVehicleDataNotification::~OnVehicleDataNotification() {}

void OnVehicleDataNotification::Run() {
  SDL_LOG_AUTO_TRACE();

  std::vector<ApplicationSharedPtr> notify_apps;
  std::vector<ApplicationSharedPtr>::iterator notified_app_it =
      notify_apps.begin();
  std::vector<smart_objects::SmartObject> appSO;

  custom_vehicle_data_manager_.CreateMobileMessageParams(
      (*message_)[strings::msg_params]);

  const auto& param_names = (*message_)[strings::msg_params].enumerate();
  for (const auto& name : param_names) {
    SDL_LOG_DEBUG("vehicle_data name: " << name);
    auto vehicle_data_value = (*message_)[strings::msg_params][name].asInt();
    application_manager_.IviInfoUpdated(name, vehicle_data_value);

    auto subscribed_to_ivi_predicate = [&name](const ApplicationSharedPtr app) {
      DCHECK_OR_RETURN(app, false);
      auto& ext = VehicleInfoAppExtension::ExtractVIExtension(*app);
      return ext.isSubscribedToVehicleInfo(name);
    };

    const auto& applications = application_manager::FindAllApps(
        application_manager_.applications(), subscribed_to_ivi_predicate);
    for (const auto& app : applications) {
      if (!app) {
        SDL_LOG_ERROR("NULL pointer");
        continue;
      }
      notified_app_it = find(notify_apps.begin(), notify_apps.end(), app);
      if (notified_app_it == notify_apps.end()) {
        notify_apps.push_back(app);
        smart_objects::SmartObject msg_param =
            smart_objects::SmartObject(smart_objects::SmartType_Map);
        msg_param[name] = (*message_)[strings::msg_params][name];
        appSO.push_back(msg_param);
      } else {
        size_t idx = std::distance(notify_apps.begin(), notified_app_it);
        appSO[idx][name] = (*message_)[strings::msg_params][name];
      }
    }
  }

  for (size_t idx = 0; idx < notify_apps.size(); idx++) {
    CommandParametersPermissions params_permissions;
    application_manager_.CheckPolicyPermissions(
        notify_apps[idx],
        window_id(),
        MessageHelper::StringifiedFunctionID(
            mobile_api::FunctionID::OnVehicleDataID),
        appSO[idx].enumerate(),
        &params_permissions);
    if (parameters_permissions_.allowed_params.empty() &&
        parameters_permissions_.disallowed_params.empty() &&
        parameters_permissions_.undefined_params.empty()) {
      SDL_LOG_DEBUG(
          "No parameter permissions provided, all params are allowed");
    }
    else {
      for (const auto& param : appSO[idx].enumerate()) {
        const auto& allowed_params = params_permissions.allowed_params;
        auto param_allowed = allowed_params.find(param);
        if (allowed_params.end() == param_allowed) {
          SDL_LOG_DEBUG("Param " << param
                                 << " is not allowed by policy for app "
                                 << notify_apps[idx]->app_id()
                                 << ". It will be ignored.");
          appSO[idx].erase(param);
        }
      }
    }

    if (appSO[idx].empty()) {
      SDL_LOG_DEBUG("App " << notify_apps[idx]->app_id()
                           << " will be skipped: there is nothing to notify.");
      continue;
    }

    SDL_LOG_INFO("Send OnVehicleData notification to "
                 << notify_apps[idx]->name().c_str() << " application id "
                 << notify_apps[idx]->app_id());
    (*message_)[strings::params][strings::connection_key] =
        notify_apps[idx]->app_id();
    (*message_)[strings::msg_params] = appSO[idx];
    SendNotification();
  }
}

}  // namespace commands
}  // namespace vehicle_info_plugin
<|endoftext|>


<commit_before>/*

  Copyright 2015 University of Helsinki

  Bug reports for this file should go to:
  Tino Didriksen <mail@tinodidriksen.com>
  Code adapted from https://github.com/TinoDidriksen/trie-tools

  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.

*/

/*
	Tests up to 16 variations of each input token:
	- Verbatim
	- With leading non-alphanumerics removed
	- With trailing non-alphanumerics removed
	- With leading and trailing non-alphanumerics removed
	- Lower-case of all the above
	- First-upper of all the above
*/

#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <algorithm>
#include <unordered_set>
#include <map>
#include <sstream>
#include <stdexcept>
#include <cmath>
#include <cerrno>
#include <cctype>

#define U_CHARSET_IS_UTF8 1
#include <unicode/uclean.h>
#include <unicode/ucnv.h>
#include <unicode/uloc.h>
#include <unicode/uchar.h>
#include <unicode/unistr.h>

#include "ZHfstOspeller.h"

using hfst_ospell::ZHfstOspeller;
using hfst_ospell::Transducer;

typedef std::map<UnicodeString,bool> valid_words_t;
valid_words_t valid_words;

struct word_t {
	size_t start, count;
	UnicodeString buffer;
};
std::vector<word_t> words(16);
std::string buffer;
std::vector<std::string> alts;
std::unordered_set<std::string> outputs;
UnicodeString ubuffer, uc_buffer;
size_t cw;

bool verbatim = false;
bool uc_first = false;
bool uc_all = true;

bool find_alternatives(ZHfstOspeller& speller, size_t suggs) {
	outputs.clear();
	alts.clear();

	// Gather corrections from all the tried variants, starting with verbatim and increasing mangling from there
	for (size_t k=0 ; k < cw && alts.size()<suggs ; ++k) {
		buffer.clear();
		words[k].buffer.toUTF8String(buffer);
		hfst_ospell::CorrectionQueue corrections = speller.suggest(buffer);

		if (corrections.size() == 0) {
			continue;
		}

		// Because speller.set_queue_limit() doesn't actually work, hard limit it here
		for (size_t i=0, e=corrections.size() ; i<e && alts.size()<suggs ; ++i) {

			buffer.clear();
			if (k != 0) {
				words[0].buffer.tempSubString(0, words[k].start).toUTF8String(buffer);
			}
			if (uc_all) {
				UnicodeString::fromUTF8(corrections.top().first).toUpper().toUTF8String(buffer);
			}
			else if (uc_first) {
				uc_buffer.setTo(UnicodeString::fromUTF8(corrections.top().first));
				ubuffer.setTo(uc_buffer, 0, 1);
				ubuffer.toUpper();
				ubuffer.append(uc_buffer, 1, uc_buffer.length()-1);
				ubuffer.toUTF8String(buffer);
			}
			else {
				buffer.append(corrections.top().first);
			}
			if (k != 0) {
				words[0].buffer.tempSubString(words[k].start + words[k].count).toUTF8String(buffer);
			}

			if (outputs.count(buffer) == 0) {
				alts.push_back(buffer);
			}
			outputs.insert(buffer);
			corrections.pop();
		}
	}

	if (!alts.empty()) {
		std::cout << "&";
		for (auto& alt : alts) {
			std::cout << "\t" << alt;
		}
		std::cout << std::endl;
		return true;
	}

	return false;
}

bool is_valid_word(ZHfstOspeller& speller, const std::string& word, size_t suggs) {
	ubuffer.setTo(UnicodeString::fromUTF8(word));

	if (word.size() == 13 && word[5] == 'D' && word == "nuvviDspeller") {
		uc_first = false;
		uc_all = false;
		words[0].start = 0;
		words[0].count = ubuffer.length();
		words[0].buffer = ubuffer;
		cw = 1;
		return false;
	}

	uc_first = false;
	uc_all = true;
	bool has_letters = false;
	for (int32_t i=0 ; i<ubuffer.length() ; ++i) {
		if (u_isalpha(ubuffer[i])) {
			has_letters = true;
			if (u_isupper(ubuffer[i]) && uc_all) {
				uc_first = true;
			}
			else if (u_islower(ubuffer[i])) {
				uc_all = false;
				break;
			}
		}
	}

	// If there are no letters in this token, just ignore it
	if (has_letters == false) {
		return true;
	}

	size_t ichStart = 0, cchUse = ubuffer.length();
	const UChar *pwsz = ubuffer.getTerminatedBuffer();

	// Always test the full given input
	words[0].buffer.remove();
	words[0].start = ichStart;
	words[0].count = cchUse;
	words[0].buffer = ubuffer;
	cw = 1;

	if (cchUse > 1 && !verbatim) {
		size_t count = cchUse;
		while (count && !u_isalnum(pwsz[ichStart+count-1])) {
			--count;
		}
		if (count != cchUse) {
			// If the input ended with non-alphanumerics, test input with non-alphanumerics trimmed from the end
			words[cw].buffer.remove();
			words[cw].start = ichStart;
			words[cw].count = count;
			words[cw].buffer.append(pwsz, words[cw].start, words[cw].count);
			++cw;
		}

		size_t start = ichStart, count2 = cchUse;
		while (start < ichStart+cchUse && !u_isalnum(pwsz[start])) {
			++start;
			--count2;
		}
		if (start != ichStart) {
			// If the input started with non-alphanumerics, test input with non-alphanumerics trimmed from the start
			words[cw].buffer.remove();
			words[cw].start = start;
			words[cw].count = count2;
			words[cw].buffer.append(pwsz, words[cw].start, words[cw].count);
			++cw;
		}

		if (start != ichStart && count != cchUse) {
			// If the input both started and ended with non-alphanumerics, test input with non-alphanumerics trimmed from both sides
			words[cw].buffer.remove();
			words[cw].start = start;
			words[cw].count = count - (cchUse - count2);
			words[cw].buffer.append(pwsz, words[cw].start, words[cw].count);
			++cw;
		}
	}

	for (size_t i=0, e=cw ; i<e ; ++i) {
		// If we are looking for suggestions, don't use the cache
		valid_words_t::iterator it = suggs ? valid_words.end() : valid_words.find(words[i].buffer);

		if (it == valid_words.end()) {
			buffer.clear();
			words[i].buffer.toUTF8String(buffer);
			bool valid = speller.spell(buffer);
			it = valid_words.insert(std::make_pair(words[i].buffer,valid)).first;

			if (!valid && !verbatim) {
				// If the word was not valid, fold it to lower case and try again
				buffer.clear();
				ubuffer = words[i].buffer;
				ubuffer.toLower();
				ubuffer.toUTF8String(buffer);

				// Add the lower case variant to the list so that we get suggestions using that, if need be
				words[cw].start = words[i].start;
				words[cw].count = words[i].count;
				words[cw].buffer = ubuffer;
				++cw;

				// Don't try again if the lower cased variant has already been tried
				valid_words_t::iterator itl = suggs ? valid_words.end() : valid_words.find(ubuffer);
				if (itl != valid_words.end()) {
					it->second = itl->second;
					it = itl;
				}
				else {
					valid = speller.spell(buffer);
					it->second = valid; // Also mark the original mixed case variant as whatever the lower cased one was
					it = valid_words.insert(std::make_pair(words[i].buffer,valid)).first;
				}
			}

			if (!valid && !verbatim && (uc_all || uc_first)) {
				// If the word was still not valid but had upper case, try a first-upper variant
				buffer.clear();
				ubuffer.setTo(words[i].buffer, 0, 1);
				ubuffer.toUpper();
				uc_buffer.setTo(words[i].buffer, 1);
				uc_buffer.toLower();
				ubuffer.append(uc_buffer);
				ubuffer.toUTF8String(buffer);

				// Add the first-upper variant to the list so that we get suggestions using that, if need be
				words[cw].start = words[i].start;
				words[cw].count = words[i].count;
				words[cw].buffer = ubuffer;
				++cw;

				// Don't try again if the first-upper variant has already been tried
				valid_words_t::iterator itl = suggs ? valid_words.end() : valid_words.find(ubuffer);
				if (itl != valid_words.end()) {
					it->second = itl->second;
					it = itl;
				}
				else {
					valid = speller.spell(buffer);
					it->second = valid; // Also mark the original mixed case variant as whatever the first-upper one was
					it = valid_words.insert(std::make_pair(words[i].buffer,valid)).first;
				}
			}
		}

		if (it->second == true) {
			return true;
		}
	}

	return false;
}

int zhfst_spell(const char* zhfst_filename) {
	ZHfstOspeller speller;
	try {
		speller.read_zhfst(zhfst_filename);
		speller.set_time_cutoff(6.0);
	}
	catch (hfst_ospell::ZHfstMetaDataParsingError zhmdpe) {
		fprintf(stderr, "cannot finish reading zhfst archive %s:\n%s.\n", zhfst_filename, zhmdpe.what());
		return EXIT_FAILURE;
	}
	catch (hfst_ospell::ZHfstZipReadingError zhzre) {
		fprintf(stderr, "cannot read zhfst archive %s:\n%s.\n", zhfst_filename, zhzre.what());
		return EXIT_FAILURE;
	}
	catch (hfst_ospell::ZHfstXmlParsingError zhxpe) {
		fprintf(stderr, "Cannot finish reading index.xml from %s:\n%s.\n", zhfst_filename, zhxpe.what());
		return EXIT_FAILURE;
	}

	std::cout << "@@ hfst-ospell-office is alive" << std::endl;

	std::string line;
	std::string word;
	std::istringstream ss;
	while (std::getline(std::cin, line)) {
		while (!line.empty() && std::isspace(line[line.size()-1])) {
			line.resize(line.size()-1);
		}
		if (line.empty()) {
			continue;
		}
		// Just in case anyone decides to use the speller for a minor eternity
		if (valid_words.size() > 20480) {
			valid_words.clear();
		}

		ss.clear();
		ss.str(line);
		size_t suggs = 0;
		char c = 0;
		if (!(ss >> suggs) || !ss.get(c) || !std::getline(ss, line)) {
			std::cout << "!" << std::endl;
			continue;
		}

		if (is_valid_word(speller, line, suggs)) {
			std::cout << "*" << std::endl;
			continue;
		}

		if (!suggs || !find_alternatives(speller, suggs)) {
			std::cout << "#" << std::endl;
		}
	}
    return EXIT_SUCCESS;
}

int main(int argc, char **argv) {
	UErrorCode status = U_ZERO_ERROR;
	u_init(&status);
	if (U_FAILURE(status) && status != U_FILE_ACCESS_ERROR) {
		std::cerr << "Error: Cannot initialize ICU. Status = " << u_errorName(status) << std::endl;
		return -1;
	}

	ucnv_setDefaultName("UTF-8");
	uloc_setDefault("en_US_POSIX", &status);

	std::vector<std::string> args(argv, argv+argc);
	for (std::vector<std::string>::iterator it=args.begin() ; it != args.end() ; ) {
		if (*it == "--verbatim") {
			verbatim = true;
			it = args.erase(it);
		}
		else {
			++it;
		}
	}

	if (args.size() < 2) {
		throw std::invalid_argument("Must pass a zhfst as argument");
	}

	int rv = zhfst_spell(args[1].c_str());

	u_cleanup();
	return rv;
}
<commit_msg>ICU is namespaced in newer versions (fixes #42)<commit_after>/*

  Copyright 2015 University of Helsinki

  Bug reports for this file should go to:
  Tino Didriksen <mail@tinodidriksen.com>
  Code adapted from https://github.com/TinoDidriksen/trie-tools

  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.

*/

/*
	Tests up to 16 variations of each input token:
	- Verbatim
	- With leading non-alphanumerics removed
	- With trailing non-alphanumerics removed
	- With leading and trailing non-alphanumerics removed
	- Lower-case of all the above
	- First-upper of all the above
*/

#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <algorithm>
#include <unordered_set>
#include <map>
#include <sstream>
#include <stdexcept>
#include <cmath>
#include <cerrno>
#include <cctype>

#define U_CHARSET_IS_UTF8 1
#include <unicode/uclean.h>
#include <unicode/ucnv.h>
#include <unicode/uloc.h>
#include <unicode/uchar.h>
#include <unicode/unistr.h>
using namespace icu;

#include "ZHfstOspeller.h"

using hfst_ospell::ZHfstOspeller;
using hfst_ospell::Transducer;

typedef std::map<UnicodeString,bool> valid_words_t;
valid_words_t valid_words;

struct word_t {
	size_t start, count;
	UnicodeString buffer;
};
std::vector<word_t> words(16);
std::string buffer;
std::vector<std::string> alts;
std::unordered_set<std::string> outputs;
UnicodeString ubuffer, uc_buffer;
size_t cw;

bool verbatim = false;
bool uc_first = false;
bool uc_all = true;

bool find_alternatives(ZHfstOspeller& speller, size_t suggs) {
	outputs.clear();
	alts.clear();

	// Gather corrections from all the tried variants, starting with verbatim and increasing mangling from there
	for (size_t k=0 ; k < cw && alts.size()<suggs ; ++k) {
		buffer.clear();
		words[k].buffer.toUTF8String(buffer);
		hfst_ospell::CorrectionQueue corrections = speller.suggest(buffer);

		if (corrections.size() == 0) {
			continue;
		}

		// Because speller.set_queue_limit() doesn't actually work, hard limit it here
		for (size_t i=0, e=corrections.size() ; i<e && alts.size()<suggs ; ++i) {

			buffer.clear();
			if (k != 0) {
				words[0].buffer.tempSubString(0, words[k].start).toUTF8String(buffer);
			}
			if (uc_all) {
				UnicodeString::fromUTF8(corrections.top().first).toUpper().toUTF8String(buffer);
			}
			else if (uc_first) {
				uc_buffer.setTo(UnicodeString::fromUTF8(corrections.top().first));
				ubuffer.setTo(uc_buffer, 0, 1);
				ubuffer.toUpper();
				ubuffer.append(uc_buffer, 1, uc_buffer.length()-1);
				ubuffer.toUTF8String(buffer);
			}
			else {
				buffer.append(corrections.top().first);
			}
			if (k != 0) {
				words[0].buffer.tempSubString(words[k].start + words[k].count).toUTF8String(buffer);
			}

			if (outputs.count(buffer) == 0) {
				alts.push_back(buffer);
			}
			outputs.insert(buffer);
			corrections.pop();
		}
	}

	if (!alts.empty()) {
		std::cout << "&";
		for (auto& alt : alts) {
			std::cout << "\t" << alt;
		}
		std::cout << std::endl;
		return true;
	}

	return false;
}

bool is_valid_word(ZHfstOspeller& speller, const std::string& word, size_t suggs) {
	ubuffer.setTo(UnicodeString::fromUTF8(word));

	if (word.size() == 13 && word[5] == 'D' && word == "nuvviDspeller") {
		uc_first = false;
		uc_all = false;
		words[0].start = 0;
		words[0].count = ubuffer.length();
		words[0].buffer = ubuffer;
		cw = 1;
		return false;
	}

	uc_first = false;
	uc_all = true;
	bool has_letters = false;
	for (int32_t i=0 ; i<ubuffer.length() ; ++i) {
		if (u_isalpha(ubuffer[i])) {
			has_letters = true;
			if (u_isupper(ubuffer[i]) && uc_all) {
				uc_first = true;
			}
			else if (u_islower(ubuffer[i])) {
				uc_all = false;
				break;
			}
		}
	}

	// If there are no letters in this token, just ignore it
	if (has_letters == false) {
		return true;
	}

	size_t ichStart = 0, cchUse = ubuffer.length();
	const UChar *pwsz = ubuffer.getTerminatedBuffer();

	// Always test the full given input
	words[0].buffer.remove();
	words[0].start = ichStart;
	words[0].count = cchUse;
	words[0].buffer = ubuffer;
	cw = 1;

	if (cchUse > 1 && !verbatim) {
		size_t count = cchUse;
		while (count && !u_isalnum(pwsz[ichStart+count-1])) {
			--count;
		}
		if (count != cchUse) {
			// If the input ended with non-alphanumerics, test input with non-alphanumerics trimmed from the end
			words[cw].buffer.remove();
			words[cw].start = ichStart;
			words[cw].count = count;
			words[cw].buffer.append(pwsz, words[cw].start, words[cw].count);
			++cw;
		}

		size_t start = ichStart, count2 = cchUse;
		while (start < ichStart+cchUse && !u_isalnum(pwsz[start])) {
			++start;
			--count2;
		}
		if (start != ichStart) {
			// If the input started with non-alphanumerics, test input with non-alphanumerics trimmed from the start
			words[cw].buffer.remove();
			words[cw].start = start;
			words[cw].count = count2;
			words[cw].buffer.append(pwsz, words[cw].start, words[cw].count);
			++cw;
		}

		if (start != ichStart && count != cchUse) {
			// If the input both started and ended with non-alphanumerics, test input with non-alphanumerics trimmed from both sides
			words[cw].buffer.remove();
			words[cw].start = start;
			words[cw].count = count - (cchUse - count2);
			words[cw].buffer.append(pwsz, words[cw].start, words[cw].count);
			++cw;
		}
	}

	for (size_t i=0, e=cw ; i<e ; ++i) {
		// If we are looking for suggestions, don't use the cache
		valid_words_t::iterator it = suggs ? valid_words.end() : valid_words.find(words[i].buffer);

		if (it == valid_words.end()) {
			buffer.clear();
			words[i].buffer.toUTF8String(buffer);
			bool valid = speller.spell(buffer);
			it = valid_words.insert(std::make_pair(words[i].buffer,valid)).first;

			if (!valid && !verbatim) {
				// If the word was not valid, fold it to lower case and try again
				buffer.clear();
				ubuffer = words[i].buffer;
				ubuffer.toLower();
				ubuffer.toUTF8String(buffer);

				// Add the lower case variant to the list so that we get suggestions using that, if need be
				words[cw].start = words[i].start;
				words[cw].count = words[i].count;
				words[cw].buffer = ubuffer;
				++cw;

				// Don't try again if the lower cased variant has already been tried
				valid_words_t::iterator itl = suggs ? valid_words.end() : valid_words.find(ubuffer);
				if (itl != valid_words.end()) {
					it->second = itl->second;
					it = itl;
				}
				else {
					valid = speller.spell(buffer);
					it->second = valid; // Also mark the original mixed case variant as whatever the lower cased one was
					it = valid_words.insert(std::make_pair(words[i].buffer,valid)).first;
				}
			}

			if (!valid && !verbatim && (uc_all || uc_first)) {
				// If the word was still not valid but had upper case, try a first-upper variant
				buffer.clear();
				ubuffer.setTo(words[i].buffer, 0, 1);
				ubuffer.toUpper();
				uc_buffer.setTo(words[i].buffer, 1);
				uc_buffer.toLower();
				ubuffer.append(uc_buffer);
				ubuffer.toUTF8String(buffer);

				// Add the first-upper variant to the list so that we get suggestions using that, if need be
				words[cw].start = words[i].start;
				words[cw].count = words[i].count;
				words[cw].buffer = ubuffer;
				++cw;

				// Don't try again if the first-upper variant has already been tried
				valid_words_t::iterator itl = suggs ? valid_words.end() : valid_words.find(ubuffer);
				if (itl != valid_words.end()) {
					it->second = itl->second;
					it = itl;
				}
				else {
					valid = speller.spell(buffer);
					it->second = valid; // Also mark the original mixed case variant as whatever the first-upper one was
					it = valid_words.insert(std::make_pair(words[i].buffer,valid)).first;
				}
			}
		}

		if (it->second == true) {
			return true;
		}
	}

	return false;
}

int zhfst_spell(const char* zhfst_filename) {
	ZHfstOspeller speller;
	try {
		speller.read_zhfst(zhfst_filename);
		speller.set_time_cutoff(6.0);
	}
	catch (hfst_ospell::ZHfstMetaDataParsingError zhmdpe) {
		fprintf(stderr, "cannot finish reading zhfst archive %s:\n%s.\n", zhfst_filename, zhmdpe.what());
		return EXIT_FAILURE;
	}
	catch (hfst_ospell::ZHfstZipReadingError zhzre) {
		fprintf(stderr, "cannot read zhfst archive %s:\n%s.\n", zhfst_filename, zhzre.what());
		return EXIT_FAILURE;
	}
	catch (hfst_ospell::ZHfstXmlParsingError zhxpe) {
		fprintf(stderr, "Cannot finish reading index.xml from %s:\n%s.\n", zhfst_filename, zhxpe.what());
		return EXIT_FAILURE;
	}

	std::cout << "@@ hfst-ospell-office is alive" << std::endl;

	std::string line;
	std::string word;
	std::istringstream ss;
	while (std::getline(std::cin, line)) {
		while (!line.empty() && std::isspace(line[line.size()-1])) {
			line.resize(line.size()-1);
		}
		if (line.empty()) {
			continue;
		}
		// Just in case anyone decides to use the speller for a minor eternity
		if (valid_words.size() > 20480) {
			valid_words.clear();
		}

		ss.clear();
		ss.str(line);
		size_t suggs = 0;
		char c = 0;
		if (!(ss >> suggs) || !ss.get(c) || !std::getline(ss, line)) {
			std::cout << "!" << std::endl;
			continue;
		}

		if (is_valid_word(speller, line, suggs)) {
			std::cout << "*" << std::endl;
			continue;
		}

		if (!suggs || !find_alternatives(speller, suggs)) {
			std::cout << "#" << std::endl;
		}
	}
    return EXIT_SUCCESS;
}

int main(int argc, char **argv) {
	UErrorCode status = U_ZERO_ERROR;
	u_init(&status);
	if (U_FAILURE(status) && status != U_FILE_ACCESS_ERROR) {
		std::cerr << "Error: Cannot initialize ICU. Status = " << u_errorName(status) << std::endl;
		return -1;
	}

	ucnv_setDefaultName("UTF-8");
	uloc_setDefault("en_US_POSIX", &status);

	std::vector<std::string> args(argv, argv+argc);
	for (std::vector<std::string>::iterator it=args.begin() ; it != args.end() ; ) {
		if (*it == "--verbatim") {
			verbatim = true;
			it = args.erase(it);
		}
		else {
			++it;
		}
	}

	if (args.size() < 2) {
		throw std::invalid_argument("Must pass a zhfst as argument");
	}

	int rv = zhfst_spell(args[1].c_str());

	u_cleanup();
	return rv;
}
<|endoftext|>


<commit_before>#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include "capsicum.h"
#include "capsicum-test.h"

// Check an open call works and close the resulting fd.
#define EXPECT_OPEN_OK(f) do { \
    int fd = f;                \
    EXPECT_OK(fd);             \
    close(fd);                 \
  } while (0)

// Test openat(2) in a variety of sitations to ensure that it obeys Capsicum
// "strict relative" rules:
//
// 1. Use strict relative lookups in capability mode or when operating
//    relative to a capability.
// 2. When performing strict relative lookups, absolute paths (including
//    symlinks to absolute paths) are not allowed, nor are paths containing
//    '..' components.
FORK_TEST(Openat, Relative) {
  int etc = open("/etc/", O_RDONLY);
  EXPECT_OK(etc);

  cap_rights_t r_base;
  cap_rights_init(&r_base, CAP_READ, CAP_WRITE, CAP_SEEK, CAP_LOOKUP);
  cap_rights_t r_ro;
  cap_rights_init(&r_ro, CAP_READ);
  cap_rights_t r_rl;
  cap_rights_init(&r_rl, CAP_READ, CAP_LOOKUP);

  int etc_cap = dup(etc);
  EXPECT_OK(etc_cap);
  EXPECT_OK(cap_rights_limit(etc_cap, &r_ro));
  int etc_cap_ro = dup(etc);
  EXPECT_OK(etc_cap_ro);
  EXPECT_OK(cap_rights_limit(etc_cap_ro, &r_rl));
  int etc_cap_base = dup(etc);
  EXPECT_OK(etc_cap_base);
  EXPECT_OK(cap_rights_limit(etc_cap_base, &r_base));

  // openat(2) with regular file descriptors in non-capability mode
  // Should Just Work (tm).
  EXPECT_OPEN_OK(openat(etc, "/etc/passwd", O_RDONLY));
  EXPECT_OPEN_OK(openat(AT_FDCWD, "/etc/passwd", O_RDONLY));
  EXPECT_OPEN_OK(openat(etc, "passwd", O_RDONLY));
  EXPECT_OPEN_OK(openat(etc, "../etc/passwd", O_RDONLY));

  // Lookups relative to capabilities should be strictly relative.
  // When not in capability mode, we don't actually require CAP_LOOKUP.
  EXPECT_OPEN_OK(openat(etc_cap_ro, "passwd", O_RDONLY));
  EXPECT_OPEN_OK(openat(etc_cap_base, "passwd", O_RDONLY));

  // Performing openat(2) on a path with leading slash ignores
  // the provided directory FD.
  EXPECT_OPEN_OK(openat(etc_cap_ro, "/etc/passwd", O_RDONLY));
  EXPECT_OPEN_OK(openat(etc_cap_base, "/etc/passwd", O_RDONLY));
  // Relative lookups that go upward are not allowed.
  EXPECT_NOTCAPABLE(openat(etc_cap_ro, "../etc/passwd", O_RDONLY));
  EXPECT_NOTCAPABLE(openat(etc_cap_base, "../etc/passwd", O_RDONLY));

  // A file opened relative to a capability should itself be a capability.
  int fd = openat(etc_cap_base, "passwd", O_RDONLY);
  EXPECT_OK(fd);
  cap_rights_t rights;
  EXPECT_OK(cap_rights_get(fd, &rights));
  EXPECT_RIGHTS_IN(&rights, &r_base);
  close(fd);

  // Enter capability mode; now ALL lookups are strictly relative.
  EXPECT_OK(cap_enter());

  // Relative lookups on regular files or capabilities with CAP_LOOKUP
  // ought to succeed.
  EXPECT_OPEN_OK(openat(etc, "passwd", O_RDONLY));
  EXPECT_OPEN_OK(openat(etc_cap_ro, "passwd", O_RDONLY));
  EXPECT_OPEN_OK(openat(etc_cap_base, "passwd", O_RDONLY));

  // Lookup relative to capabilities without CAP_LOOKUP should fail.
  EXPECT_NOTCAPABLE(openat(etc_cap, "passwd", O_RDONLY));

  // Absolute lookups should fail.
  EXPECT_CAPMODE(openat(AT_FDCWD, "/etc/passwd", O_RDONLY));
  EXPECT_NOTCAPABLE(openat(etc, "/etc/passwd", O_RDONLY));
  EXPECT_NOTCAPABLE(openat(etc_cap_ro, "/etc/passwd", O_RDONLY));

  // Lookups containing '..' should fail in capability mode.
  EXPECT_NOTCAPABLE(openat(etc, "../etc/passwd", O_RDONLY));
  EXPECT_NOTCAPABLE(openat(etc_cap_ro, "../etc/passwd", O_RDONLY));
  EXPECT_NOTCAPABLE(openat(etc_cap_base, "../etc/passwd", O_RDONLY));

  fd = openat(etc, "passwd", O_RDONLY);
  EXPECT_OK(fd);

  // A file opened relative to a capability should itself be a capability.
  fd = openat(etc_cap_base, "passwd", O_RDONLY);
  EXPECT_OK(fd);
  EXPECT_OK(cap_rights_get(fd, &rights));
  EXPECT_RIGHTS_IN(&rights, &r_base);
  close(fd);

  fd = openat(etc_cap_ro, "passwd", O_RDONLY);
  EXPECT_OK(fd);
  EXPECT_OK(cap_rights_get(fd, &rights));
  EXPECT_RIGHTS_IN(&rights, &r_rl);
  close(fd);
}

TEST(Openat, Subdir) {
  // Create a couple of nested directories
  int rc = mkdir("/tmp/cap_topdir", 0755);
  EXPECT_OK(rc);
  if (rc < 0 && errno != EEXIST) return;
  rc = mkdir("/tmp/cap_topdir/cap_subdir", 0755);
  EXPECT_OK(rc);
  if (rc < 0 && errno != EEXIST) return;

  int cap_dir = open("/tmp/cap_topdir", O_RDONLY);
  EXPECT_OK(cap_dir);
  cap_rights_t r_rl;
  cap_rights_init(&r_rl, CAP_READ, CAP_LOOKUP);
  EXPECT_OK(cap_rights_limit(cap_dir, &r_rl));

  // Check that we can't escape the top directory by the cunning
  // ruse of going via a subdirectory.
  EXPECT_NOTCAPABLE(openat(cap_dir, "cap_subdir/../../etc/passwd", O_RDONLY));

  pid_t child = fork();
  if (child == 0) {
    EXPECT_OK(cap_enter());  // Enter capability mode
    EXPECT_NOTCAPABLE(openat(cap_dir, "cap_subdir/../../etc/passwd", O_RDONLY));
    exit(HasFailure());
  }
  int status;
  EXPECT_EQ(child, waitpid(child, &status, 0));
  rc = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
  EXPECT_EQ(0, rc);
  // Tidy up.
  rmdir("/tmp/cap_topdir/cap_subdir");
  rmdir("/tmp/cap_topdir");
}

#define SYMLINK_DIR "/tmp/cap_openat_symlink"
TEST(Openat, RelativeSymlink) {
  // Prepare a directory containing:
  //  -rw-rw-r--  normal
  //  lrwxrwxrwx  symlink.absolute_in -> /tmp/cap_openat_symlink/normal
  //  lrwxrwxrwx  symlink.absolute_out -> /etc/passwd
  //  lrwxrwxrwx  symlink.normal -> normal
  //  lrwxrwxrwx  symlink.relative_in -> ../../tmp/cap_openat_symlink/normal
  //  lrwxrwxrwx  symlink.relative_out -> ../../etc/passwd
  int rc = mkdir(SYMLINK_DIR, 0755);
  EXPECT_OK(rc);
  if (rc < 0 && errno != EEXIST) return;
  int dir_fd = open(SYMLINK_DIR, O_RDONLY);
  EXPECT_OK(dir_fd);
  int cap_dir = dup(dir_fd);
  EXPECT_OK(cap_dir);
  cap_rights_t r_rl;
  cap_rights_init(&r_rl, CAP_READ, CAP_LOOKUP);
  EXPECT_OK(cap_rights_limit(cap_dir, &r_rl));
  int normal = open(SYMLINK_DIR "/normal", O_CREAT|O_RDWR, 0644);
  EXPECT_OK(normal);
  const char *contents = "Hello world\n";
  EXPECT_OK(write(normal, contents, strlen(contents)));
  close(normal);

  EXPECT_OK(symlink(SYMLINK_DIR "/normal", SYMLINK_DIR "/symlink.absolute_in"));
  EXPECT_OK(symlink("/etc/passwd", SYMLINK_DIR "/symlink.absolute_out"));
  EXPECT_OK(symlink("normal", SYMLINK_DIR "/symlink.normal"));
  EXPECT_OK(symlink("../.." SYMLINK_DIR "/normal", SYMLINK_DIR "/symlink.relative_in"));
  EXPECT_OK(symlink("../../etc/passwd", SYMLINK_DIR "/symlink.relative_out"));

  // Any kind of symlink can be opened relative to an ordinary directory FD.
  EXPECT_OPEN_OK(openat(dir_fd, "symlink.normal", O_RDONLY));
  EXPECT_OPEN_OK(openat(dir_fd, "symlink.absolute_in", O_RDONLY));
  EXPECT_OPEN_OK(openat(dir_fd, "symlink.absolute_out", O_RDONLY));
  EXPECT_OPEN_OK(openat(dir_fd, "symlink.relative_in", O_RDONLY));
  EXPECT_OPEN_OK(openat(dir_fd, "symlink.relative_out", O_RDONLY));

  // Even when not in capability mode, should only be able to open symlinks that
  // stay within the directory.
  EXPECT_OPEN_OK(openat(cap_dir, "symlink.normal", O_RDONLY));
  EXPECT_NOTCAPABLE(openat(cap_dir, "symlink.absolute_in", O_RDONLY));
  EXPECT_NOTCAPABLE(openat(cap_dir, "symlink.absolute_out", O_RDONLY));
  EXPECT_NOTCAPABLE(openat(cap_dir, "symlink.relative_in", O_RDONLY));
  EXPECT_NOTCAPABLE(openat(cap_dir, "symlink.relative_out", O_RDONLY));

  int child = fork();
  if (child == 0) {
    // Child process: run the test in capability mode
    EXPECT_OK(cap_enter());

    // Only symlink within the directory can be opened relative to an ordinary directory FD.
    EXPECT_OPEN_OK(openat(dir_fd, "normal", O_RDONLY));
    EXPECT_OPEN_OK(openat(dir_fd, "symlink.normal", O_RDONLY));
    EXPECT_NOTCAPABLE(openat(dir_fd, "symlink.absolute_in", O_RDONLY));
    EXPECT_NOTCAPABLE(openat(dir_fd, "symlink.absolute_out", O_RDONLY));
    EXPECT_NOTCAPABLE(openat(dir_fd, "symlink.relative_in", O_RDONLY));
    EXPECT_NOTCAPABLE(openat(dir_fd, "symlink.relative_out", O_RDONLY));

    // Only symlink within the directory can be opened relative to an ordinary directory FD.
    EXPECT_OPEN_OK(openat(cap_dir, "normal", O_RDONLY));
    EXPECT_OPEN_OK(openat(cap_dir, "symlink.normal", O_RDONLY));
    EXPECT_NOTCAPABLE(openat(cap_dir, "symlink.absolute_in", O_RDONLY));
    EXPECT_NOTCAPABLE(openat(cap_dir, "symlink.absolute_out", O_RDONLY));
    EXPECT_NOTCAPABLE(openat(cap_dir, "symlink.relative_in", O_RDONLY));
    EXPECT_NOTCAPABLE(openat(cap_dir, "symlink.relative_out", O_RDONLY));
    exit(HasFailure());
  }
  int status;
  EXPECT_EQ(child, waitpid(child, &status, 0));
  rc = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
  EXPECT_EQ(0, rc);

  // Tidy up
  unlink(SYMLINK_DIR "/symlink.absolute_in");
  unlink(SYMLINK_DIR "/symlink.absolute_out");
  unlink(SYMLINK_DIR "/symlink.normal");
  unlink(SYMLINK_DIR "/symlink.relative_in");
  unlink(SYMLINK_DIR "/symlink.relative_out");
  unlink(SYMLINK_DIR "/normal");
  rmdir(SYMLINK_DIR);
}
<commit_msg>Check that subrights are inherited across openat(2)<commit_after>#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>

#include "capsicum.h"
#include "capsicum-test.h"

// Check an open call works and close the resulting fd.
#define EXPECT_OPEN_OK(f) do { \
    int fd = f;                \
    EXPECT_OK(fd);             \
    close(fd);                 \
  } while (0)

// Test openat(2) in a variety of sitations to ensure that it obeys Capsicum
// "strict relative" rules:
//
// 1. Use strict relative lookups in capability mode or when operating
//    relative to a capability.
// 2. When performing strict relative lookups, absolute paths (including
//    symlinks to absolute paths) are not allowed, nor are paths containing
//    '..' components.
FORK_TEST(Openat, Relative) {
  int etc = open("/etc/", O_RDONLY);
  EXPECT_OK(etc);

  cap_rights_t r_base;
  cap_rights_init(&r_base, CAP_READ, CAP_WRITE, CAP_SEEK, CAP_LOOKUP, CAP_FCNTL, CAP_IOCTL);
  cap_rights_t r_ro;
  cap_rights_init(&r_ro, CAP_READ);
  cap_rights_t r_rl;
  cap_rights_init(&r_rl, CAP_READ, CAP_LOOKUP);

  int etc_cap = dup(etc);
  EXPECT_OK(etc_cap);
  EXPECT_OK(cap_rights_limit(etc_cap, &r_ro));
  int etc_cap_ro = dup(etc);
  EXPECT_OK(etc_cap_ro);
  EXPECT_OK(cap_rights_limit(etc_cap_ro, &r_rl));
  int etc_cap_base = dup(etc);
  EXPECT_OK(etc_cap_base);
  EXPECT_OK(cap_rights_limit(etc_cap_base, &r_base));
#ifdef HAVE_CAP_FCNTLS_LIMIT
  // Also limit fcntl(2) subrights.
  EXPECT_OK(cap_fcntls_limit(etc_cap_base, CAP_FCNTL_GETFL));
#endif
#ifdef HAVE_CAP_IOCTLS_LIMIT
  // Also limit ioctl(2) subrights.
  unsigned long ioctl_nread = FIONREAD;
  EXPECT_OK(cap_ioctls_limit(etc_cap_base, &ioctl_nread, 1));
#endif

  // openat(2) with regular file descriptors in non-capability mode
  // Should Just Work (tm).
  EXPECT_OPEN_OK(openat(etc, "/etc/passwd", O_RDONLY));
  EXPECT_OPEN_OK(openat(AT_FDCWD, "/etc/passwd", O_RDONLY));
  EXPECT_OPEN_OK(openat(etc, "passwd", O_RDONLY));
  EXPECT_OPEN_OK(openat(etc, "../etc/passwd", O_RDONLY));

  // Lookups relative to capabilities should be strictly relative.
  // When not in capability mode, we don't actually require CAP_LOOKUP.
  EXPECT_OPEN_OK(openat(etc_cap_ro, "passwd", O_RDONLY));
  EXPECT_OPEN_OK(openat(etc_cap_base, "passwd", O_RDONLY));

  // Performing openat(2) on a path with leading slash ignores
  // the provided directory FD.
  EXPECT_OPEN_OK(openat(etc_cap_ro, "/etc/passwd", O_RDONLY));
  EXPECT_OPEN_OK(openat(etc_cap_base, "/etc/passwd", O_RDONLY));
  // Relative lookups that go upward are not allowed.
  EXPECT_NOTCAPABLE(openat(etc_cap_ro, "../etc/passwd", O_RDONLY));
  EXPECT_NOTCAPABLE(openat(etc_cap_base, "../etc/passwd", O_RDONLY));

  // A file opened relative to a capability should itself be a capability.
  int fd = openat(etc_cap_base, "passwd", O_RDONLY);
  EXPECT_OK(fd);
  cap_rights_t rights;
  EXPECT_OK(cap_rights_get(fd, &rights));
  EXPECT_RIGHTS_IN(&rights, &r_base);
#ifdef HAVE_CAP_FCNTLS_LIMIT
  uint32_t fcntls;
  EXPECT_OK(cap_fcntls_get(fd, &fcntls));
  EXPECT_EQ(CAP_FCNTL_GETFL, fcntls);
#endif
#ifdef HAVE_CAP_IOCTLS_LIMIT
  unsigned long ioctls[16];
  ssize_t nioctls;
  memset(ioctls, 0, sizeof(ioctls));
  nioctls = cap_ioctls_get(fd, ioctls, 16);
  EXPECT_OK(nioctls);
  EXPECT_EQ(1, nioctls);
  EXPECT_EQ(FIONREAD, ioctls[0]);
#endif
  close(fd);

  // Enter capability mode; now ALL lookups are strictly relative.
  EXPECT_OK(cap_enter());

  // Relative lookups on regular files or capabilities with CAP_LOOKUP
  // ought to succeed.
  EXPECT_OPEN_OK(openat(etc, "passwd", O_RDONLY));
  EXPECT_OPEN_OK(openat(etc_cap_ro, "passwd", O_RDONLY));
  EXPECT_OPEN_OK(openat(etc_cap_base, "passwd", O_RDONLY));

  // Lookup relative to capabilities without CAP_LOOKUP should fail.
  EXPECT_NOTCAPABLE(openat(etc_cap, "passwd", O_RDONLY));

  // Absolute lookups should fail.
  EXPECT_CAPMODE(openat(AT_FDCWD, "/etc/passwd", O_RDONLY));
  EXPECT_NOTCAPABLE(openat(etc, "/etc/passwd", O_RDONLY));
  EXPECT_NOTCAPABLE(openat(etc_cap_ro, "/etc/passwd", O_RDONLY));

  // Lookups containing '..' should fail in capability mode.
  EXPECT_NOTCAPABLE(openat(etc, "../etc/passwd", O_RDONLY));
  EXPECT_NOTCAPABLE(openat(etc_cap_ro, "../etc/passwd", O_RDONLY));
  EXPECT_NOTCAPABLE(openat(etc_cap_base, "../etc/passwd", O_RDONLY));

  fd = openat(etc, "passwd", O_RDONLY);
  EXPECT_OK(fd);

  // A file opened relative to a capability should itself be a capability.
  fd = openat(etc_cap_base, "passwd", O_RDONLY);
  EXPECT_OK(fd);
  EXPECT_OK(cap_rights_get(fd, &rights));
  EXPECT_RIGHTS_IN(&rights, &r_base);
  close(fd);

  fd = openat(etc_cap_ro, "passwd", O_RDONLY);
  EXPECT_OK(fd);
  EXPECT_OK(cap_rights_get(fd, &rights));
  EXPECT_RIGHTS_IN(&rights, &r_rl);
  close(fd);
}

TEST(Openat, Subdir) {
  // Create a couple of nested directories
  int rc = mkdir("/tmp/cap_topdir", 0755);
  EXPECT_OK(rc);
  if (rc < 0 && errno != EEXIST) return;
  rc = mkdir("/tmp/cap_topdir/cap_subdir", 0755);
  EXPECT_OK(rc);
  if (rc < 0 && errno != EEXIST) return;

  int cap_dir = open("/tmp/cap_topdir", O_RDONLY);
  EXPECT_OK(cap_dir);
  cap_rights_t r_rl;
  cap_rights_init(&r_rl, CAP_READ, CAP_LOOKUP);
  EXPECT_OK(cap_rights_limit(cap_dir, &r_rl));

  // Check that we can't escape the top directory by the cunning
  // ruse of going via a subdirectory.
  EXPECT_NOTCAPABLE(openat(cap_dir, "cap_subdir/../../etc/passwd", O_RDONLY));

  pid_t child = fork();
  if (child == 0) {
    EXPECT_OK(cap_enter());  // Enter capability mode
    EXPECT_NOTCAPABLE(openat(cap_dir, "cap_subdir/../../etc/passwd", O_RDONLY));
    exit(HasFailure());
  }
  int status;
  EXPECT_EQ(child, waitpid(child, &status, 0));
  rc = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
  EXPECT_EQ(0, rc);
  // Tidy up.
  rmdir("/tmp/cap_topdir/cap_subdir");
  rmdir("/tmp/cap_topdir");
}

#define SYMLINK_DIR "/tmp/cap_openat_symlink"
TEST(Openat, RelativeSymlink) {
  // Prepare a directory containing:
  //  -rw-rw-r--  normal
  //  lrwxrwxrwx  symlink.absolute_in -> /tmp/cap_openat_symlink/normal
  //  lrwxrwxrwx  symlink.absolute_out -> /etc/passwd
  //  lrwxrwxrwx  symlink.normal -> normal
  //  lrwxrwxrwx  symlink.relative_in -> ../../tmp/cap_openat_symlink/normal
  //  lrwxrwxrwx  symlink.relative_out -> ../../etc/passwd
  int rc = mkdir(SYMLINK_DIR, 0755);
  EXPECT_OK(rc);
  if (rc < 0 && errno != EEXIST) return;
  int dir_fd = open(SYMLINK_DIR, O_RDONLY);
  EXPECT_OK(dir_fd);
  int cap_dir = dup(dir_fd);
  EXPECT_OK(cap_dir);
  cap_rights_t r_rl;
  cap_rights_init(&r_rl, CAP_READ, CAP_LOOKUP);
  EXPECT_OK(cap_rights_limit(cap_dir, &r_rl));
  int normal = open(SYMLINK_DIR "/normal", O_CREAT|O_RDWR, 0644);
  EXPECT_OK(normal);
  const char *contents = "Hello world\n";
  EXPECT_OK(write(normal, contents, strlen(contents)));
  close(normal);

  EXPECT_OK(symlink(SYMLINK_DIR "/normal", SYMLINK_DIR "/symlink.absolute_in"));
  EXPECT_OK(symlink("/etc/passwd", SYMLINK_DIR "/symlink.absolute_out"));
  EXPECT_OK(symlink("normal", SYMLINK_DIR "/symlink.normal"));
  EXPECT_OK(symlink("../.." SYMLINK_DIR "/normal", SYMLINK_DIR "/symlink.relative_in"));
  EXPECT_OK(symlink("../../etc/passwd", SYMLINK_DIR "/symlink.relative_out"));

  // Any kind of symlink can be opened relative to an ordinary directory FD.
  EXPECT_OPEN_OK(openat(dir_fd, "symlink.normal", O_RDONLY));
  EXPECT_OPEN_OK(openat(dir_fd, "symlink.absolute_in", O_RDONLY));
  EXPECT_OPEN_OK(openat(dir_fd, "symlink.absolute_out", O_RDONLY));
  EXPECT_OPEN_OK(openat(dir_fd, "symlink.relative_in", O_RDONLY));
  EXPECT_OPEN_OK(openat(dir_fd, "symlink.relative_out", O_RDONLY));

  // Even when not in capability mode, should only be able to open symlinks that
  // stay within the directory.
  EXPECT_OPEN_OK(openat(cap_dir, "symlink.normal", O_RDONLY));
  EXPECT_NOTCAPABLE(openat(cap_dir, "symlink.absolute_in", O_RDONLY));
  EXPECT_NOTCAPABLE(openat(cap_dir, "symlink.absolute_out", O_RDONLY));
  EXPECT_NOTCAPABLE(openat(cap_dir, "symlink.relative_in", O_RDONLY));
  EXPECT_NOTCAPABLE(openat(cap_dir, "symlink.relative_out", O_RDONLY));

  int child = fork();
  if (child == 0) {
    // Child process: run the test in capability mode
    EXPECT_OK(cap_enter());

    // Only symlink within the directory can be opened relative to an ordinary directory FD.
    EXPECT_OPEN_OK(openat(dir_fd, "normal", O_RDONLY));
    EXPECT_OPEN_OK(openat(dir_fd, "symlink.normal", O_RDONLY));
    EXPECT_NOTCAPABLE(openat(dir_fd, "symlink.absolute_in", O_RDONLY));
    EXPECT_NOTCAPABLE(openat(dir_fd, "symlink.absolute_out", O_RDONLY));
    EXPECT_NOTCAPABLE(openat(dir_fd, "symlink.relative_in", O_RDONLY));
    EXPECT_NOTCAPABLE(openat(dir_fd, "symlink.relative_out", O_RDONLY));

    // Only symlink within the directory can be opened relative to an ordinary directory FD.
    EXPECT_OPEN_OK(openat(cap_dir, "normal", O_RDONLY));
    EXPECT_OPEN_OK(openat(cap_dir, "symlink.normal", O_RDONLY));
    EXPECT_NOTCAPABLE(openat(cap_dir, "symlink.absolute_in", O_RDONLY));
    EXPECT_NOTCAPABLE(openat(cap_dir, "symlink.absolute_out", O_RDONLY));
    EXPECT_NOTCAPABLE(openat(cap_dir, "symlink.relative_in", O_RDONLY));
    EXPECT_NOTCAPABLE(openat(cap_dir, "symlink.relative_out", O_RDONLY));
    exit(HasFailure());
  }
  int status;
  EXPECT_EQ(child, waitpid(child, &status, 0));
  rc = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
  EXPECT_EQ(0, rc);

  // Tidy up
  unlink(SYMLINK_DIR "/symlink.absolute_in");
  unlink(SYMLINK_DIR "/symlink.absolute_out");
  unlink(SYMLINK_DIR "/symlink.normal");
  unlink(SYMLINK_DIR "/symlink.relative_in");
  unlink(SYMLINK_DIR "/symlink.relative_out");
  unlink(SYMLINK_DIR "/normal");
  rmdir(SYMLINK_DIR);
}
<|endoftext|>


<commit_before>/**
 * @copyright
 * ====================================================================
 * Copyright (c) 2003-2007 CollabNet.  All rights reserved.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution.  The terms
 * are also available at http://subversion.tigris.org/license-1.html.
 * If newer versions of this license are posted there, you may use a
 * newer version instead, at your option.
 *
 * This software consists of voluntary contributions made by many
 * individuals.  For exact contribution history, see the revision
 * history and logs, available at http://subversion.tigris.org/.
 * ====================================================================
 * @endcopyright
 *
 * @file Pool.cpp
 * @brief Implementation of the class Pool
 */

#include "Pool.h"
#include "JNIUtil.h"
#include "JNIMutex.h"
#include "JNICriticalSection.h"
#include "svn_pools.h"

/**
 * Constructor to create one apr pool as the subpool of the global pool
 * store this pool as the request pool.
 */
Pool::Pool()
{
  JNICriticalSection criticalSection(*JNIUtil::getGlobalPoolMutex());
  m_pool = svn_pool_create(JNIUtil::getPool());
  JNIUtil::setRequestPool(this);
}

/**
 * Destructor to destroy the apr pool and to clear the request pool
 * pointer.
 */
Pool::~Pool()
{
  JNICriticalSection criticalSection(*JNIUtil::getGlobalPoolMutex());
  JNIUtil::setRequestPool(NULL);
  if (m_pool)
    svn_pool_destroy (m_pool);
}
<commit_msg>Formatting tweak, no functional change.<commit_after>/**
 * @copyright
 * ====================================================================
 * Copyright (c) 2003-2007 CollabNet.  All rights reserved.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution.  The terms
 * are also available at http://subversion.tigris.org/license-1.html.
 * If newer versions of this license are posted there, you may use a
 * newer version instead, at your option.
 *
 * This software consists of voluntary contributions made by many
 * individuals.  For exact contribution history, see the revision
 * history and logs, available at http://subversion.tigris.org/.
 * ====================================================================
 * @endcopyright
 *
 * @file Pool.cpp
 * @brief Implementation of the class Pool
 */

#include "Pool.h"
#include "JNIUtil.h"
#include "JNIMutex.h"
#include "JNICriticalSection.h"
#include "svn_pools.h"

/**
 * Constructor to create one apr pool as the subpool of the global pool
 * store this pool as the request pool.
 */
Pool::Pool()
{
  JNICriticalSection criticalSection(*JNIUtil::getGlobalPoolMutex());
  m_pool = svn_pool_create(JNIUtil::getPool());
  JNIUtil::setRequestPool(this);
}

/**
 * Destructor to destroy the apr pool and to clear the request pool
 * pointer.
 */
Pool::~Pool()
{
  JNICriticalSection criticalSection(*JNIUtil::getGlobalPoolMutex());
  JNIUtil::setRequestPool(NULL);
  if (m_pool)
    svn_pool_destroy(m_pool);
}
<|endoftext|>


<commit_before>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

#include <svtools/framestatuslistener.hxx>
#include <com/sun/star/frame/XDispatchProvider.hpp>
#include <com/sun/star/lang/DisposedException.hpp>
#include <osl/mutex.hxx>
#include <vcl/svapp.hxx>

using namespace ::cppu;
using namespace ::com::sun::star::awt;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::util;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::frame;

namespace svt
{

FrameStatusListener::FrameStatusListener(
    const Reference< XMultiServiceFactory >& rServiceManager,
    const Reference< XFrame >& xFrame ) :
    OWeakObject()
    ,   m_bInitialized( sal_True )
    ,   m_bDisposed( sal_False )
    ,   m_xFrame( xFrame )
    ,   m_xServiceManager( rServiceManager )
{
}

FrameStatusListener::~FrameStatusListener()
{
}

// XInterface
Any SAL_CALL FrameStatusListener::queryInterface( const Type& rType )
throw ( RuntimeException )
{
    Any a = ::cppu::queryInterface(
                rType ,
                static_cast< XComponent* >( this ),
                static_cast< XFrameActionListener* >( this ),
                static_cast< XStatusListener* >( this ),
                static_cast< XEventListener* >( static_cast< XStatusListener* >( this )),
                static_cast< XEventListener* >( static_cast< XFrameActionListener* >( this )));

    if ( a.hasValue() )
        return a;

    return OWeakObject::queryInterface( rType );
}

void SAL_CALL FrameStatusListener::acquire() throw ()
{
    OWeakObject::acquire();
}

void SAL_CALL FrameStatusListener::release() throw ()
{
    OWeakObject::release();
}

// XComponent
void SAL_CALL FrameStatusListener::dispose()
throw (::com::sun::star::uno::RuntimeException)
{
    Reference< XComponent > xThis( static_cast< OWeakObject* >(this), UNO_QUERY );

    SolarMutexGuard aSolarMutexGuard;
    if ( m_bDisposed )
        throw DisposedException();

    Reference< XStatusListener > xStatusListener( static_cast< OWeakObject* >( this ), UNO_QUERY );
    URLToDispatchMap::iterator pIter = m_aListenerMap.begin();
    while ( pIter != m_aListenerMap.end() )
    {
        try
        {
            Reference< XDispatch > xDispatch( pIter->second );
            Reference< XURLTransformer > xURLTransformer( m_xServiceManager->createInstance(
                                                            rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
                                                                "com.sun.star.util.URLTransformer" ))),
                                                          UNO_QUERY );
            com::sun::star::util::URL aTargetURL;
            aTargetURL.Complete = pIter->first;
            xURLTransformer->parseStrict( aTargetURL );

            if ( xDispatch.is() && xStatusListener.is() )
                xDispatch->removeStatusListener( xStatusListener, aTargetURL );
        }
        catch ( Exception& )
        {
        }

        ++pIter;
    }

    m_bDisposed = sal_True;
}

void SAL_CALL FrameStatusListener::addEventListener( const Reference< XEventListener >& )
throw ( RuntimeException )
{
    // helper class for status updates - no need to support listener
}

void SAL_CALL FrameStatusListener::removeEventListener( const Reference< XEventListener >& )
throw ( RuntimeException )
{
    // helper class for status updates - no need to support listener
}

// XEventListener
void SAL_CALL FrameStatusListener::disposing( const EventObject& Source )
throw ( RuntimeException )
{
    Reference< XInterface > xSource( Source.Source );

    SolarMutexGuard aSolarMutexGuard;

    URLToDispatchMap::iterator pIter = m_aListenerMap.begin();
    while ( pIter != m_aListenerMap.end() )
    {
        // Compare references and release dispatch references if they are equal.
        Reference< XInterface > xIfac( pIter->second, UNO_QUERY );
        if ( xSource == xIfac )
            pIter->second.clear();
    }

    Reference< XInterface > xIfac( m_xFrame, UNO_QUERY );
    if ( xIfac == xSource )
        m_xFrame.clear();
}

void FrameStatusListener::frameAction( const FrameActionEvent& Action )
throw ( RuntimeException )
{
    if ( Action.Action == FrameAction_CONTEXT_CHANGED )
        bindListener();
}

void FrameStatusListener::addStatusListener( const rtl::OUString& aCommandURL )
{
    Reference< XDispatch >       xDispatch;
    Reference< XStatusListener > xStatusListener;
    com::sun::star::util::URL    aTargetURL;

    {
        SolarMutexGuard aSolarMutexGuard;
        URLToDispatchMap::iterator pIter = m_aListenerMap.find( aCommandURL );

        // Already in the list of status listener. Do nothing.
        if ( pIter != m_aListenerMap.end() )
            return;

        // Check if we are already initialized. Implementation starts adding itself as status listener when
        // intialize is called.
        if ( !m_bInitialized )
        {
            // Put into the boost::unordered_map of status listener. Will be activated when initialized is called
            m_aListenerMap.insert( URLToDispatchMap::value_type( aCommandURL, Reference< XDispatch >() ));
            return;
        }
        else
        {
            // Add status listener directly as intialize has already been called.
            Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY );
            if ( m_xServiceManager.is() && xDispatchProvider.is() )
            {
                Reference< XURLTransformer > xURLTransformer( m_xServiceManager->createInstance(
                                                                rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.util.URLTransformer" ))),
                                                            UNO_QUERY );
                aTargetURL.Complete = aCommandURL;
                xURLTransformer->parseStrict( aTargetURL );
                xDispatch = xDispatchProvider->queryDispatch( aTargetURL, ::rtl::OUString(), 0 );

                xStatusListener = Reference< XStatusListener >( static_cast< OWeakObject* >( this ), UNO_QUERY );
                URLToDispatchMap::iterator aIter = m_aListenerMap.find( aCommandURL );
                if ( aIter != m_aListenerMap.end() )
                {
                    Reference< XDispatch > xOldDispatch( aIter->second );
                    aIter->second = xDispatch;

                    try
                    {
                        if ( xOldDispatch.is() )
                            xOldDispatch->removeStatusListener( xStatusListener, aTargetURL );
                    }
                    catch ( Exception& )
                    {
                    }
                }
                else
                    m_aListenerMap.insert( URLToDispatchMap::value_type( aCommandURL, xDispatch ));
            }
        }
    }

    // Call without locked mutex as we are called back from dispatch implementation
    try
    {
        if ( xDispatch.is() )
            xDispatch->addStatusListener( xStatusListener, aTargetURL );
    }
    catch ( Exception& )
    {
    }
}

void FrameStatusListener::removeStatusListener( const rtl::OUString& aCommandURL )
{
    SolarMutexGuard aSolarMutexGuard;

    URLToDispatchMap::iterator pIter = m_aListenerMap.find( aCommandURL );
    if ( pIter != m_aListenerMap.end() )
    {
        Reference< XDispatch > xDispatch( pIter->second );
        Reference< XStatusListener > xStatusListener( static_cast< OWeakObject* >( this ), UNO_QUERY );
        m_aListenerMap.erase( pIter );

        try
        {
            Reference< XURLTransformer > xURLTransformer( m_xServiceManager->createInstance(
                                                            rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.util.URLTransformer" ))),
                                                        UNO_QUERY );
            com::sun::star::util::URL aTargetURL;
            aTargetURL.Complete = aCommandURL;
            xURLTransformer->parseStrict( aTargetURL );

            if ( xDispatch.is() && xStatusListener.is() )
                xDispatch->removeStatusListener( xStatusListener, aTargetURL );
        }
        catch ( Exception& )
        {
        }
    }
}

void FrameStatusListener::bindListener()
{
    std::vector< Listener > aDispatchVector;
    Reference< XStatusListener > xStatusListener;

    {
        SolarMutexGuard aSolarMutexGuard;

        if ( !m_bInitialized )
            return;

        // Collect all registered command URL's and store them temporary
        Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY );
        if ( m_xServiceManager.is() && xDispatchProvider.is() )
        {
            xStatusListener = Reference< XStatusListener >( static_cast< OWeakObject* >( this ), UNO_QUERY );
            URLToDispatchMap::iterator pIter = m_aListenerMap.begin();
            while ( pIter != m_aListenerMap.end() )
            {
                Reference< XURLTransformer > xURLTransformer( m_xServiceManager->createInstance(
                                                                rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.util.URLTransformer" ))),
                                                            UNO_QUERY );
                com::sun::star::util::URL aTargetURL;
                aTargetURL.Complete = pIter->first;
                xURLTransformer->parseStrict( aTargetURL );

                Reference< XDispatch > xDispatch( pIter->second );
                if ( xDispatch.is() )
                {
                    // We already have a dispatch object => we have to requery.
                    // Release old dispatch object and remove it as listener
                    try
                    {
                        xDispatch->removeStatusListener( xStatusListener, aTargetURL );
                    }
                    catch ( Exception& )
                    {
                    }
                }

                // Query for dispatch object. Old dispatch will be released with this, too.
                try
                {
                    xDispatch = xDispatchProvider->queryDispatch( aTargetURL, ::rtl::OUString(), 0 );
                }
                catch ( Exception& )
                {
                }
                pIter->second = xDispatch;

                Listener aListener( aTargetURL, xDispatch );
                aDispatchVector.push_back( aListener );
                ++pIter;
            }
        }
    }

    // Call without locked mutex as we are called back from dispatch implementation
    if ( xStatusListener.is() )
    {
        try
        {
            for ( sal_uInt32 i = 0; i < aDispatchVector.size(); i++ )
            {
                Listener& rListener = aDispatchVector[i];
                if ( rListener.xDispatch.is() )
                    rListener.xDispatch->addStatusListener( xStatusListener, rListener.aURL );
            }
        }
        catch ( Exception& )
        {
        }
    }
}

void FrameStatusListener::unbindListener()
{
    SolarMutexGuard aSolarMutexGuard;

    if ( !m_bInitialized )
        return;

    // Collect all registered command URL's and store them temporary
    Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY );
    if ( m_xServiceManager.is() && xDispatchProvider.is() )
    {
        Reference< XStatusListener > xStatusListener( static_cast< OWeakObject* >( this ), UNO_QUERY );
        URLToDispatchMap::iterator pIter = m_aListenerMap.begin();
        while ( pIter != m_aListenerMap.end() )
        {
            Reference< XURLTransformer > xURLTransformer( m_xServiceManager->createInstance(
                                                            rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.util.URLTransformer" ))),
                                                        UNO_QUERY );
            com::sun::star::util::URL aTargetURL;
            aTargetURL.Complete = pIter->first;
            xURLTransformer->parseStrict( aTargetURL );

            Reference< XDispatch > xDispatch( pIter->second );
            if ( xDispatch.is() )
            {
                // We already have a dispatch object => we have to requery.
                // Release old dispatch object and remove it as listener
                try
                {
                    xDispatch->removeStatusListener( xStatusListener, aTargetURL );
                }
                catch ( Exception& )
                {
                }
            }
            pIter->second.clear();
            ++pIter;
        }
    }
}

} // svt

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<commit_msg>catch by const ref<commit_after>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

#include <svtools/framestatuslistener.hxx>
#include <com/sun/star/frame/XDispatchProvider.hpp>
#include <com/sun/star/lang/DisposedException.hpp>
#include <osl/mutex.hxx>
#include <vcl/svapp.hxx>

using namespace ::cppu;
using namespace ::com::sun::star::awt;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::util;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::frame;

namespace svt
{

FrameStatusListener::FrameStatusListener(
    const Reference< XMultiServiceFactory >& rServiceManager,
    const Reference< XFrame >& xFrame ) :
    OWeakObject()
    ,   m_bInitialized( sal_True )
    ,   m_bDisposed( sal_False )
    ,   m_xFrame( xFrame )
    ,   m_xServiceManager( rServiceManager )
{
}

FrameStatusListener::~FrameStatusListener()
{
}

// XInterface
Any SAL_CALL FrameStatusListener::queryInterface( const Type& rType )
throw ( RuntimeException )
{
    Any a = ::cppu::queryInterface(
                rType ,
                static_cast< XComponent* >( this ),
                static_cast< XFrameActionListener* >( this ),
                static_cast< XStatusListener* >( this ),
                static_cast< XEventListener* >( static_cast< XStatusListener* >( this )),
                static_cast< XEventListener* >( static_cast< XFrameActionListener* >( this )));

    if ( a.hasValue() )
        return a;

    return OWeakObject::queryInterface( rType );
}

void SAL_CALL FrameStatusListener::acquire() throw ()
{
    OWeakObject::acquire();
}

void SAL_CALL FrameStatusListener::release() throw ()
{
    OWeakObject::release();
}

// XComponent
void SAL_CALL FrameStatusListener::dispose()
throw (::com::sun::star::uno::RuntimeException)
{
    Reference< XComponent > xThis( static_cast< OWeakObject* >(this), UNO_QUERY );

    SolarMutexGuard aSolarMutexGuard;
    if ( m_bDisposed )
        throw DisposedException();

    Reference< XStatusListener > xStatusListener( static_cast< OWeakObject* >( this ), UNO_QUERY );
    URLToDispatchMap::iterator pIter = m_aListenerMap.begin();
    while ( pIter != m_aListenerMap.end() )
    {
        try
        {
            Reference< XDispatch > xDispatch( pIter->second );
            Reference< XURLTransformer > xURLTransformer( m_xServiceManager->createInstance(
                                                            rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
                                                                "com.sun.star.util.URLTransformer" ))),
                                                          UNO_QUERY );
            com::sun::star::util::URL aTargetURL;
            aTargetURL.Complete = pIter->first;
            xURLTransformer->parseStrict( aTargetURL );

            if ( xDispatch.is() && xStatusListener.is() )
                xDispatch->removeStatusListener( xStatusListener, aTargetURL );
        }
        catch (const Exception&)
        {
        }

        ++pIter;
    }

    m_bDisposed = sal_True;
}

void SAL_CALL FrameStatusListener::addEventListener( const Reference< XEventListener >& )
throw ( RuntimeException )
{
    // helper class for status updates - no need to support listener
}

void SAL_CALL FrameStatusListener::removeEventListener( const Reference< XEventListener >& )
throw ( RuntimeException )
{
    // helper class for status updates - no need to support listener
}

// XEventListener
void SAL_CALL FrameStatusListener::disposing( const EventObject& Source )
throw ( RuntimeException )
{
    Reference< XInterface > xSource( Source.Source );

    SolarMutexGuard aSolarMutexGuard;

    URLToDispatchMap::iterator pIter = m_aListenerMap.begin();
    while ( pIter != m_aListenerMap.end() )
    {
        // Compare references and release dispatch references if they are equal.
        Reference< XInterface > xIfac( pIter->second, UNO_QUERY );
        if ( xSource == xIfac )
            pIter->second.clear();
    }

    Reference< XInterface > xIfac( m_xFrame, UNO_QUERY );
    if ( xIfac == xSource )
        m_xFrame.clear();
}

void FrameStatusListener::frameAction( const FrameActionEvent& Action )
throw ( RuntimeException )
{
    if ( Action.Action == FrameAction_CONTEXT_CHANGED )
        bindListener();
}

void FrameStatusListener::addStatusListener( const rtl::OUString& aCommandURL )
{
    Reference< XDispatch >       xDispatch;
    Reference< XStatusListener > xStatusListener;
    com::sun::star::util::URL    aTargetURL;

    {
        SolarMutexGuard aSolarMutexGuard;
        URLToDispatchMap::iterator pIter = m_aListenerMap.find( aCommandURL );

        // Already in the list of status listener. Do nothing.
        if ( pIter != m_aListenerMap.end() )
            return;

        // Check if we are already initialized. Implementation starts adding itself as status listener when
        // intialize is called.
        if ( !m_bInitialized )
        {
            // Put into the boost::unordered_map of status listener. Will be activated when initialized is called
            m_aListenerMap.insert( URLToDispatchMap::value_type( aCommandURL, Reference< XDispatch >() ));
            return;
        }
        else
        {
            // Add status listener directly as intialize has already been called.
            Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY );
            if ( m_xServiceManager.is() && xDispatchProvider.is() )
            {
                Reference< XURLTransformer > xURLTransformer( m_xServiceManager->createInstance(
                                                                rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.util.URLTransformer" ))),
                                                            UNO_QUERY );
                aTargetURL.Complete = aCommandURL;
                xURLTransformer->parseStrict( aTargetURL );
                xDispatch = xDispatchProvider->queryDispatch( aTargetURL, ::rtl::OUString(), 0 );

                xStatusListener = Reference< XStatusListener >( static_cast< OWeakObject* >( this ), UNO_QUERY );
                URLToDispatchMap::iterator aIter = m_aListenerMap.find( aCommandURL );
                if ( aIter != m_aListenerMap.end() )
                {
                    Reference< XDispatch > xOldDispatch( aIter->second );
                    aIter->second = xDispatch;

                    try
                    {
                        if ( xOldDispatch.is() )
                            xOldDispatch->removeStatusListener( xStatusListener, aTargetURL );
                    }
                    catch (const Exception&)
                    {
                    }
                }
                else
                    m_aListenerMap.insert( URLToDispatchMap::value_type( aCommandURL, xDispatch ));
            }
        }
    }

    // Call without locked mutex as we are called back from dispatch implementation
    try
    {
        if ( xDispatch.is() )
            xDispatch->addStatusListener( xStatusListener, aTargetURL );
    }
    catch (const Exception&)
    {
    }
}

void FrameStatusListener::removeStatusListener( const rtl::OUString& aCommandURL )
{
    SolarMutexGuard aSolarMutexGuard;

    URLToDispatchMap::iterator pIter = m_aListenerMap.find( aCommandURL );
    if ( pIter != m_aListenerMap.end() )
    {
        Reference< XDispatch > xDispatch( pIter->second );
        Reference< XStatusListener > xStatusListener( static_cast< OWeakObject* >( this ), UNO_QUERY );
        m_aListenerMap.erase( pIter );

        try
        {
            Reference< XURLTransformer > xURLTransformer( m_xServiceManager->createInstance(
                                                            rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.util.URLTransformer" ))),
                                                        UNO_QUERY );
            com::sun::star::util::URL aTargetURL;
            aTargetURL.Complete = aCommandURL;
            xURLTransformer->parseStrict( aTargetURL );

            if ( xDispatch.is() && xStatusListener.is() )
                xDispatch->removeStatusListener( xStatusListener, aTargetURL );
        }
        catch (const Exception&)
        {
        }
    }
}

void FrameStatusListener::bindListener()
{
    std::vector< Listener > aDispatchVector;
    Reference< XStatusListener > xStatusListener;

    {
        SolarMutexGuard aSolarMutexGuard;

        if ( !m_bInitialized )
            return;

        // Collect all registered command URL's and store them temporary
        Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY );
        if ( m_xServiceManager.is() && xDispatchProvider.is() )
        {
            xStatusListener = Reference< XStatusListener >( static_cast< OWeakObject* >( this ), UNO_QUERY );
            URLToDispatchMap::iterator pIter = m_aListenerMap.begin();
            while ( pIter != m_aListenerMap.end() )
            {
                Reference< XURLTransformer > xURLTransformer( m_xServiceManager->createInstance(
                                                                rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.util.URLTransformer" ))),
                                                            UNO_QUERY );
                com::sun::star::util::URL aTargetURL;
                aTargetURL.Complete = pIter->first;
                xURLTransformer->parseStrict( aTargetURL );

                Reference< XDispatch > xDispatch( pIter->second );
                if ( xDispatch.is() )
                {
                    // We already have a dispatch object => we have to requery.
                    // Release old dispatch object and remove it as listener
                    try
                    {
                        xDispatch->removeStatusListener( xStatusListener, aTargetURL );
                    }
                    catch (const Exception&)
                    {
                    }
                }

                // Query for dispatch object. Old dispatch will be released with this, too.
                try
                {
                    xDispatch = xDispatchProvider->queryDispatch( aTargetURL, ::rtl::OUString(), 0 );
                }
                catch (const Exception&)
                {
                }
                pIter->second = xDispatch;

                Listener aListener( aTargetURL, xDispatch );
                aDispatchVector.push_back( aListener );
                ++pIter;
            }
        }
    }

    // Call without locked mutex as we are called back from dispatch implementation
    if ( xStatusListener.is() )
    {
        try
        {
            for ( sal_uInt32 i = 0; i < aDispatchVector.size(); i++ )
            {
                Listener& rListener = aDispatchVector[i];
                if ( rListener.xDispatch.is() )
                    rListener.xDispatch->addStatusListener( xStatusListener, rListener.aURL );
            }
        }
        catch (const Exception&)
        {
        }
    }
}

void FrameStatusListener::unbindListener()
{
    SolarMutexGuard aSolarMutexGuard;

    if ( !m_bInitialized )
        return;

    // Collect all registered command URL's and store them temporary
    Reference< XDispatchProvider > xDispatchProvider( m_xFrame, UNO_QUERY );
    if ( m_xServiceManager.is() && xDispatchProvider.is() )
    {
        Reference< XStatusListener > xStatusListener( static_cast< OWeakObject* >( this ), UNO_QUERY );
        URLToDispatchMap::iterator pIter = m_aListenerMap.begin();
        while ( pIter != m_aListenerMap.end() )
        {
            Reference< XURLTransformer > xURLTransformer( m_xServiceManager->createInstance(
                                                            rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.util.URLTransformer" ))),
                                                        UNO_QUERY );
            com::sun::star::util::URL aTargetURL;
            aTargetURL.Complete = pIter->first;
            xURLTransformer->parseStrict( aTargetURL );

            Reference< XDispatch > xDispatch( pIter->second );
            if ( xDispatch.is() )
            {
                // We already have a dispatch object => we have to requery.
                // Release old dispatch object and remove it as listener
                try
                {
                    xDispatch->removeStatusListener( xStatusListener, aTargetURL );
                }
                catch (const Exception&)
                {
                }
            }
            pIter->second.clear();
            ++pIter;
        }
    }
}

} // svt

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<|endoftext|>


<commit_before>/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.

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.
==============================================================================*/

#define EIGEN_USE_THREADS

#include "tensorflow/compiler/xla/service/backend.h"

#include <algorithm>
#include <string>
#include <utility>

#include "absl/memory/memory.h"
#include "third_party/eigen3/unsupported/Eigen/CXX11/Tensor"
#include "tensorflow/compiler/xla/service/compiler.h"
#include "tensorflow/compiler/xla/service/platform_util.h"
#include "tensorflow/compiler/xla/status_macros.h"
#include "tensorflow/compiler/xla/statusor.h"
#include "tensorflow/compiler/xla/types.h"
#include "tensorflow/compiler/xla/util.h"
#include "tensorflow/core/common_runtime/eigen_thread_pool.h"
#include "tensorflow/core/lib/core/errors.h"
#include "tensorflow/core/lib/core/threadpool.h"
#include "tensorflow/core/platform/byte_order.h"
#include "tensorflow/core/platform/cpu_info.h"
#include "tensorflow/core/platform/env.h"
#include "tensorflow/core/platform/logging.h"
#include "tensorflow/core/platform/stream_executor_no_cuda.h"

namespace xla {

BackendOptions& BackendOptions::set_platform(se::Platform* platform) {
  platform_ = platform;
  return *this;
}

se::Platform* BackendOptions::platform() const { return platform_; }

BackendOptions& BackendOptions::set_intra_op_parallelism_threads(
    int num_threads) {
  intra_op_parallelism_threads_ = num_threads;
  return *this;
}

int BackendOptions::intra_op_parallelism_threads() const {
  return intra_op_parallelism_threads_;
}

BackendOptions& BackendOptions::set_allowed_devices(std::set<int> device_set) {
  allowed_devices_ = device_set;
  return *this;
}

std::set<int> BackendOptions::get_allowed_devices() const {
  return allowed_devices_;
}

// Define this in .cc file to avoid having to include eigen or forward declare
// these types in the header.
struct Backend::EigenThreadPoolWrapper {
  explicit EigenThreadPoolWrapper(const int num_threads)
      : pool(new tensorflow::thread::ThreadPool(tensorflow::Env::Default(),
                                                "XLAEigen", num_threads)),
        wrapper(new tensorflow::EigenThreadPoolWrapper(pool.get())),
        device(new Eigen::ThreadPoolDevice(wrapper.get(),
                                           wrapper->NumThreads())) {}

  std::unique_ptr<tensorflow::thread::ThreadPool> pool;
  std::unique_ptr<tensorflow::EigenThreadPoolWrapper> wrapper;
  std::unique_ptr<Eigen::ThreadPoolDevice> device;
};

/* static */ StatusOr<std::unique_ptr<Backend>> Backend::CreateBackend(
    const BackendOptions& options) {
  se::Platform* platform = options.platform();
  TF_ASSIGN_OR_RETURN(auto compiler, Compiler::GetForPlatform(platform));
  TF_ASSIGN_OR_RETURN(auto stream_executors,
                      PlatformUtil::GetStreamExecutors(
                          platform, options.get_allowed_devices()));
  TF_ASSIGN_OR_RETURN(auto transfer_manager,
                      TransferManager::GetForPlatform(platform));
  TF_ASSIGN_OR_RETURN(auto computation_placer,
                      ComputationPlacer::GetForPlatform(platform));
  std::unique_ptr<Backend> backend(
      new Backend(platform, compiler, stream_executors, transfer_manager,
                  computation_placer, options.intra_op_parallelism_threads()));
  return std::move(backend);
}

/* static */ StatusOr<std::unique_ptr<Backend>>
Backend::CreateDefaultBackend() {
  TF_ASSIGN_OR_RETURN(se::Platform * platform,
                      PlatformUtil::GetDefaultPlatform());
  BackendOptions backend_options;
  backend_options.set_platform(platform);
  return CreateBackend(backend_options);
}

StatusOr<StreamPool::Ptr> Backend::BorrowStream(int device_ordinal) {
  TF_ASSIGN_OR_RETURN(auto executor, stream_executor(device_ordinal));
  return BorrowStream(executor);
}

StatusOr<StreamPool::Ptr> Backend::BorrowStream(se::StreamExecutor* executor) {
  tensorflow::mutex_lock l(mu_);
  if (0 == stream_pools_.count(executor)) {
    stream_pools_.emplace(std::piecewise_construct,
                          std::forward_as_tuple(executor),
                          std::forward_as_tuple());
  }
  return stream_pools_.at(executor).BorrowStream(executor);
}

Backend::Backend(se::Platform* platform, Compiler* compiler,
                 absl::Span<se::StreamExecutor* const> stream_executors,
                 TransferManager* transfer_manager,
                 ComputationPlacer* computation_placer,
                 int intra_op_parallelism_threads)
    : platform_(platform),
      compiler_(compiler),
      transfer_manager_(transfer_manager),
      computation_placer_(computation_placer) {
  // The given set of stream executors set may include invalid executors.
  for (se::StreamExecutor* exec : stream_executors) {
    if (exec != nullptr) {
      stream_executors_.push_back(exec);
    }
  }
  // Create a memory allocator for the valid stream executors.
  memory_allocator_ = absl::make_unique<StreamExecutorMemoryAllocator>(
      platform, stream_executors);
  CHECK(!stream_executors_.empty())
      << "Service found no devices for backend " << platform_->Name() << '.';

  if (platform->id() == se::host::kHostPlatformId) {
    const int num_threads = intra_op_parallelism_threads > 0
                                ? intra_op_parallelism_threads
                                : tensorflow::port::NumSchedulableCPUs();
    intra_op_thread_pool_wrapper_.reset(
        new EigenThreadPoolWrapper(num_threads));
  }
}

Backend::~Backend() {}

int Backend::default_device_ordinal() const {
  return default_stream_executor()->device_ordinal();
}

const Eigen::ThreadPoolDevice* Backend::eigen_intra_op_thread_pool_device()
    const {
  if (intra_op_thread_pool_wrapper_ == nullptr) {
    return nullptr;
  }
  return intra_op_thread_pool_wrapper_->device.get();
}

tensorflow::thread::ThreadPool* Backend::eigen_intra_op_thread_pool() const {
  if (intra_op_thread_pool_wrapper_ == nullptr) {
    return nullptr;
  }
  return intra_op_thread_pool_wrapper_->pool.get();
}

StatusOr<se::StreamExecutor*> Backend::stream_executor(
    int device_ordinal) const {
  if (device_ordinal < 0 ||
      device_ordinal > stream_executors_.back()->device_ordinal()) {
    return InvalidArgument(
        "Invalid device ordinal value (%d). Valid range is [0, %d].",
        device_ordinal, stream_executors_.back()->device_ordinal());
  }
  for (auto* executor : stream_executors_) {
    if (executor && executor->device_ordinal() == device_ordinal) {
      return executor;
    }
  }
  return InvalidArgument("device %s not supported by XLA service",
                         device_name(device_ordinal));
}

StatusOr<bool> Backend::devices_equivalent(int device_ordinal_a,
                                           int device_ordinal_b) {
  // Use the name from device description to determine equivalence. This is a
  // bit crude but works for GPUs which is the important case where we compile
  // an executable for one GPU and want to know if it will run (well) on
  // another.
  TF_ASSIGN_OR_RETURN(se::StreamExecutor * executor_a,
                      stream_executor(device_ordinal_a));
  TF_ASSIGN_OR_RETURN(se::StreamExecutor * executor_b,
                      stream_executor(device_ordinal_b));
  return (executor_a->GetDeviceDescription().name() ==
          executor_b->GetDeviceDescription().name());
}

Status Backend::ResetDevices() {
  return transfer_manager_->ResetDevices(stream_executors_);
}

}  // namespace xla
<commit_msg>Fix include ordering in backend.cc<commit_after>/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.

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.
==============================================================================*/

#define EIGEN_USE_THREADS

#include "tensorflow/compiler/xla/service/backend.h"

#include <algorithm>
#include <string>
#include <utility>

#include "absl/memory/memory.h"
#include "tensorflow/compiler/xla/service/compiler.h"
#include "tensorflow/compiler/xla/service/platform_util.h"
#include "tensorflow/compiler/xla/status_macros.h"
#include "tensorflow/compiler/xla/statusor.h"
#include "tensorflow/compiler/xla/types.h"
#include "tensorflow/compiler/xla/util.h"
#include "tensorflow/core/common_runtime/eigen_thread_pool.h"
#include "tensorflow/core/lib/core/errors.h"
#include "tensorflow/core/lib/core/threadpool.h"
#include "tensorflow/core/platform/byte_order.h"
#include "tensorflow/core/platform/cpu_info.h"
#include "tensorflow/core/platform/env.h"
#include "tensorflow/core/platform/logging.h"
#include "tensorflow/core/platform/stream_executor_no_cuda.h"
#include "third_party/eigen3/unsupported/Eigen/CXX11/Tensor"

namespace xla {

BackendOptions& BackendOptions::set_platform(se::Platform* platform) {
  platform_ = platform;
  return *this;
}

se::Platform* BackendOptions::platform() const { return platform_; }

BackendOptions& BackendOptions::set_intra_op_parallelism_threads(
    int num_threads) {
  intra_op_parallelism_threads_ = num_threads;
  return *this;
}

int BackendOptions::intra_op_parallelism_threads() const {
  return intra_op_parallelism_threads_;
}

BackendOptions& BackendOptions::set_allowed_devices(std::set<int> device_set) {
  allowed_devices_ = device_set;
  return *this;
}

std::set<int> BackendOptions::get_allowed_devices() const {
  return allowed_devices_;
}

// Define this in .cc file to avoid having to include eigen or forward declare
// these types in the header.
struct Backend::EigenThreadPoolWrapper {
  explicit EigenThreadPoolWrapper(const int num_threads)
      : pool(new tensorflow::thread::ThreadPool(tensorflow::Env::Default(),
                                                "XLAEigen", num_threads)),
        wrapper(new tensorflow::EigenThreadPoolWrapper(pool.get())),
        device(new Eigen::ThreadPoolDevice(wrapper.get(),
                                           wrapper->NumThreads())) {}

  std::unique_ptr<tensorflow::thread::ThreadPool> pool;
  std::unique_ptr<tensorflow::EigenThreadPoolWrapper> wrapper;
  std::unique_ptr<Eigen::ThreadPoolDevice> device;
};

/* static */ StatusOr<std::unique_ptr<Backend>> Backend::CreateBackend(
    const BackendOptions& options) {
  se::Platform* platform = options.platform();
  TF_ASSIGN_OR_RETURN(auto compiler, Compiler::GetForPlatform(platform));
  TF_ASSIGN_OR_RETURN(auto stream_executors,
                      PlatformUtil::GetStreamExecutors(
                          platform, options.get_allowed_devices()));
  TF_ASSIGN_OR_RETURN(auto transfer_manager,
                      TransferManager::GetForPlatform(platform));
  TF_ASSIGN_OR_RETURN(auto computation_placer,
                      ComputationPlacer::GetForPlatform(platform));
  std::unique_ptr<Backend> backend(
      new Backend(platform, compiler, stream_executors, transfer_manager,
                  computation_placer, options.intra_op_parallelism_threads()));
  return std::move(backend);
}

/* static */ StatusOr<std::unique_ptr<Backend>>
Backend::CreateDefaultBackend() {
  TF_ASSIGN_OR_RETURN(se::Platform * platform,
                      PlatformUtil::GetDefaultPlatform());
  BackendOptions backend_options;
  backend_options.set_platform(platform);
  return CreateBackend(backend_options);
}

StatusOr<StreamPool::Ptr> Backend::BorrowStream(int device_ordinal) {
  TF_ASSIGN_OR_RETURN(auto executor, stream_executor(device_ordinal));
  return BorrowStream(executor);
}

StatusOr<StreamPool::Ptr> Backend::BorrowStream(se::StreamExecutor* executor) {
  tensorflow::mutex_lock l(mu_);
  if (0 == stream_pools_.count(executor)) {
    stream_pools_.emplace(std::piecewise_construct,
                          std::forward_as_tuple(executor),
                          std::forward_as_tuple());
  }
  return stream_pools_.at(executor).BorrowStream(executor);
}

Backend::Backend(se::Platform* platform, Compiler* compiler,
                 absl::Span<se::StreamExecutor* const> stream_executors,
                 TransferManager* transfer_manager,
                 ComputationPlacer* computation_placer,
                 int intra_op_parallelism_threads)
    : platform_(platform),
      compiler_(compiler),
      transfer_manager_(transfer_manager),
      computation_placer_(computation_placer) {
  // The given set of stream executors set may include invalid executors.
  for (se::StreamExecutor* exec : stream_executors) {
    if (exec != nullptr) {
      stream_executors_.push_back(exec);
    }
  }
  // Create a memory allocator for the valid stream executors.
  memory_allocator_ = absl::make_unique<StreamExecutorMemoryAllocator>(
      platform, stream_executors);
  CHECK(!stream_executors_.empty())
      << "Service found no devices for backend " << platform_->Name() << '.';

  if (platform->id() == se::host::kHostPlatformId) {
    const int num_threads = intra_op_parallelism_threads > 0
                                ? intra_op_parallelism_threads
                                : tensorflow::port::NumSchedulableCPUs();
    intra_op_thread_pool_wrapper_.reset(
        new EigenThreadPoolWrapper(num_threads));
  }
}

Backend::~Backend() {}

int Backend::default_device_ordinal() const {
  return default_stream_executor()->device_ordinal();
}

const Eigen::ThreadPoolDevice* Backend::eigen_intra_op_thread_pool_device()
    const {
  if (intra_op_thread_pool_wrapper_ == nullptr) {
    return nullptr;
  }
  return intra_op_thread_pool_wrapper_->device.get();
}

tensorflow::thread::ThreadPool* Backend::eigen_intra_op_thread_pool() const {
  if (intra_op_thread_pool_wrapper_ == nullptr) {
    return nullptr;
  }
  return intra_op_thread_pool_wrapper_->pool.get();
}

StatusOr<se::StreamExecutor*> Backend::stream_executor(
    int device_ordinal) const {
  if (device_ordinal < 0 ||
      device_ordinal > stream_executors_.back()->device_ordinal()) {
    return InvalidArgument(
        "Invalid device ordinal value (%d). Valid range is [0, %d].",
        device_ordinal, stream_executors_.back()->device_ordinal());
  }
  for (auto* executor : stream_executors_) {
    if (executor && executor->device_ordinal() == device_ordinal) {
      return executor;
    }
  }
  return InvalidArgument("device %s not supported by XLA service",
                         device_name(device_ordinal));
}

StatusOr<bool> Backend::devices_equivalent(int device_ordinal_a,
                                           int device_ordinal_b) {
  // Use the name from device description to determine equivalence. This is a
  // bit crude but works for GPUs which is the important case where we compile
  // an executable for one GPU and want to know if it will run (well) on
  // another.
  TF_ASSIGN_OR_RETURN(se::StreamExecutor * executor_a,
                      stream_executor(device_ordinal_a));
  TF_ASSIGN_OR_RETURN(se::StreamExecutor * executor_b,
                      stream_executor(device_ordinal_b));
  return (executor_a->GetDeviceDescription().name() ==
          executor_b->GetDeviceDescription().name());
}

Status Backend::ResetDevices() {
  return transfer_manager_->ResetDevices(stream_executors_);
}

}  // namespace xla
<|endoftext|>