From: Helge Norberg Date: Tue, 22 Nov 2016 13:40:20 +0000 (+0100) Subject: [env] Fail early with clear error message if any of the configured paths are not... X-Git-Tag: 2.1.0_Beta2~151 X-Git-Url: https://git.sesse.net/?a=commitdiff_plain;h=6e9e5d185687f2c20dbcab77b7218dcd67c98b82;p=casparcg [env] Fail early with clear error message if any of the configured paths are not creatable/writable. --- diff --git a/CHANGELOG b/CHANGELOG index 0afa3fed6..3da21ca17 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,14 @@ C H A N G E S +CasparCG 2.1.0 Next (w.r.t 2.1.0 Beta 1) +======================================== + +General +------- + + o Fail early with clear error message if configured paths are not + creatable/writable. + CasparCG 2.1.0 Beta 1 (w.r.t 2.0.7 Stable) ========================================== diff --git a/common/env.cpp b/common/env.cpp index f422fb0c0..6984e2d82 100644 --- a/common/env.cpp +++ b/common/env.cpp @@ -39,6 +39,7 @@ #include #include #include +#include namespace caspar { namespace env { @@ -57,86 +58,92 @@ void check_is_configured() CASPAR_THROW_EXCEPTION(invalid_operation() << msg_info(L"Enviroment properties has not been configured")); } +std::wstring clean_path(std::wstring path) +{ + boost::replace_all(path, L"\\\\", L"/"); + boost::replace_all(path, L"\\", L"/"); + + return path; +} + +std::wstring ensure_trailing_slash(std::wstring folder) +{ + if (folder.at(folder.length() - 1) != L'/') + folder.append(L"/"); + + return folder; +} + +std::wstring resolve_or_create(const std::wstring& folder) +{ + auto found_path = find_case_insensitive(folder); + + if (found_path) + return *found_path; + else + { + boost::system::error_code ec; + boost::filesystem::create_directories(folder, ec); + + if (ec) + CASPAR_THROW_EXCEPTION(user_error() << msg_info("Failed to create directory " + u8(folder) + " (" + ec.message() + ")")); + + return folder; + } +} + +void ensure_writable(const std::wstring& folder) +{ + static const std::wstring CREATE_FILE_TEST = L"casparcg_test_writable.empty"; + + boost::system::error_code ec; + boost::filesystem::path test_file(folder + L"/" + CREATE_FILE_TEST); + boost::filesystem::ofstream out(folder + L"/" + CREATE_FILE_TEST); + + if (out.fail()) + { + boost::filesystem::remove(test_file, ec); + CASPAR_THROW_EXCEPTION(user_error() << msg_info(L"Directory " + folder + L" is not writable.")); + } + + out.close(); + boost::filesystem::remove(test_file, ec); +} + void configure(const std::wstring& filename) { try { - initial = boost::filesystem::initial_path().wstring(); + initial = clean_path(boost::filesystem::initial_path().wstring()); boost::filesystem::wifstream file(initial + L"/" + filename); boost::property_tree::read_xml(file, pt, boost::property_tree::xml_parser::trim_whitespace | boost::property_tree::xml_parser::no_comments); auto paths = pt.get_child(L"configuration.paths"); - media = paths.get(L"media-path", initial + L"/media/"); - log = paths.get(L"log-path", initial + L"/log/"); - ftemplate = boost::filesystem::complete(paths.get(L"template-path", initial + L"/template/")).wstring(); - data = paths.get(L"data-path", initial + L"/data/"); - font = paths.get(L"font-path", initial + L"/font/"); - thumbnails = paths.get(L"thumbnail-path", initial + L"/thumbnail/"); + media = clean_path(paths.get(L"media-path", initial + L"/media/")); + log = clean_path(paths.get(L"log-path", initial + L"/log/")); + ftemplate = clean_path(boost::filesystem::complete(paths.get(L"template-path", initial + L"/template/")).wstring()); + data = clean_path(paths.get(L"data-path", initial + L"/data/")); + font = clean_path(paths.get(L"font-path", initial + L"/font/")); + thumbnails = clean_path(paths.get(L"thumbnail-path", initial + L"/thumbnail/")); } - catch(...) + catch (...) { CASPAR_LOG(error) << L" ### Invalid configuration file. ###"; throw; } - try - { - auto found_media_path = find_case_insensitive(media); - if (found_media_path) - media = *found_media_path; - else - boost::filesystem::create_directories(media); - - auto found_template_path = find_case_insensitive(ftemplate); - if (found_template_path) - ftemplate = *found_template_path; - else - boost::filesystem::create_directories(ftemplate); - - auto found_data_path = find_case_insensitive(data); - if (found_data_path) - data = *found_data_path; - else - boost::filesystem::create_directories(data); - - auto found_font_path = find_case_insensitive(font); - if (found_font_path) - font = *found_font_path; - else - boost::filesystem::create_directories(font); - - auto found_thumbnails_path = find_case_insensitive(thumbnails); - if (found_thumbnails_path) - thumbnails = *found_thumbnails_path; - else - boost::filesystem::create_directories(thumbnails); - - auto found_log_path = find_case_insensitive(log); - if (found_log_path) - log = *found_log_path; - else if (!boost::filesystem::create_directories(log)) - log = L"./"; - - //Make sure that all paths have a trailing slash - if(media.at(media.length()-1) != L'/') - media.append(L"/"); - if(log.at(log.length()-1) != L'/') - log.append(L"/"); - if(ftemplate.at(ftemplate.length()-1) != L'/') - ftemplate.append(L"/"); - if(data.at(data.length()-1) != L'/') - data.append(L"/"); - if(font.at(font.length()-1) != L'/') - font.append(L"/"); - if(thumbnails.at(thumbnails.length()-1) != L'/') - thumbnails.append(L"/"); - } - catch(...) - { - CASPAR_LOG_CURRENT_EXCEPTION(); - CASPAR_LOG(error) << L"Failed to create configured directories."; - } + media = ensure_trailing_slash(resolve_or_create(media)); + log = ensure_trailing_slash(resolve_or_create(log)); + ftemplate = ensure_trailing_slash(resolve_or_create(ftemplate)); + data = ensure_trailing_slash(resolve_or_create(data)); + font = ensure_trailing_slash(resolve_or_create(font)); + thumbnails = ensure_trailing_slash(resolve_or_create(thumbnails)); + + ensure_writable(log); + ensure_writable(ftemplate); + ensure_writable(data); + ensure_writable(thumbnails); } const std::wstring& initial_folder() diff --git a/shell/main.cpp b/shell/main.cpp index 2eac1b8b6..6ae71f153 100644 --- a/shell/main.cpp +++ b/shell/main.cpp @@ -19,9 +19,9 @@ * Author: Robert Nagy, ronag89@gmail.com */ -// tbbmalloc_proxy: -// Replace the standard memory allocation routines in Microsoft* C/C++ RTL -// (malloc/free, global new/delete, etc.) with the TBB memory allocator. +// tbbmalloc_proxy: +// Replace the standard memory allocation routines in Microsoft* C/C++ RTL +// (malloc/free, global new/delete, etc.) with the TBB memory allocator. #include "stdafx.h" @@ -73,7 +73,7 @@ #include using namespace caspar; - + void setup_global_locale() { boost::locale::generator gen; @@ -118,7 +118,7 @@ void do_run( while(true) { std::getline(std::wcin, wcmd); // TODO: It's blocking... - + //boost::to_upper(wcmd); if(boost::iequals(wcmd, L"EXIT") || boost::iequals(wcmd, L"Q") || boost::iequals(wcmd, L"QUIT") || boost::iequals(wcmd, L"BYE")) @@ -143,11 +143,11 @@ void do_run( else if(wcmd.substr(0, 1) == L"5") { auto file = wcmd.substr(2, wcmd.length()-1); - wcmd = L"PLAY 1-1 " + file + L" LOOP\r\n" - L"PLAY 1-2 " + file + L" LOOP\r\n" + wcmd = L"PLAY 1-1 " + file + L" LOOP\r\n" + L"PLAY 1-2 " + file + L" LOOP\r\n" L"PLAY 1-3 " + file + L" LOOP\r\n" - L"PLAY 2-1 " + file + L" LOOP\r\n" - L"PLAY 2-2 " + file + L" LOOP\r\n" + L"PLAY 2-1 " + file + L" LOOP\r\n" + L"PLAY 2-2 " + file + L" LOOP\r\n" L"PLAY 2-3 " + file + L" LOOP\r\n"; } else if(wcmd.substr(0, 1) == L"7") @@ -231,7 +231,7 @@ bool run(const std::wstring& config_file_name, tbb::atomic& should_wait_fo boost::property_tree::xml_writer_settings w(' ', 3); boost::property_tree::write_xml(str, env::properties(), w); CASPAR_LOG(info) << config_file_name << L":\n-----------------------------------------\n" << str.str() << L"-----------------------------------------"; - + { CASPAR_SCOPED_CONTEXT_MSG(config_file_name + L": ") caspar_server->start(); @@ -249,7 +249,7 @@ bool run(const std::wstring& config_file_name, tbb::atomic& should_wait_fo caspar_server->get_amcp_command_repository())))->create(console_client); std::weak_ptr> weak_amcp = amcp; - // Use separate thread for the blocking console input, will be terminated + // Use separate thread for the blocking console input, will be terminated // anyway when the main thread terminates. boost::thread stdin_thread(std::bind(do_run, weak_amcp, std::ref(shutdown_server_now), std::ref(should_wait_for_keypress))); //compiler didn't like lambda here... stdin_thread.detach(); @@ -278,7 +278,7 @@ int main(int argc, char** argv) setup_global_locale(); std::wcout << L"Type \"q\" to close application." << std::endl; - + // Set debug mode. auto debugging_environment = setup_debugging_environment(); @@ -300,8 +300,8 @@ int main(int argc, char** argv) tbb::task_scheduler_init init; std::wstring config_file_name(L"casparcg.config"); - - try + + try { // Configure environment properties from configuration. if (argc >= 2) @@ -323,7 +323,7 @@ int main(int argc, char** argv) log::add_file_sink(env::log_folder() + L"caspar", caspar::log::category != caspar::log::log_category::calltrace); log::add_file_sink(env::log_folder() + L"calltrace", caspar::log::category == caspar::log::log_category::calltrace); std::wcout << L"Logging [info] or higher severity to " << env::log_folder() << std::endl << std::endl; - + // Setup console window. setup_console_window(); @@ -337,7 +337,7 @@ int main(int argc, char** argv) if (thread->name != "main thread" && thread->name != "tbb-worker-thread") CASPAR_LOG(warning) << L"Thread left running: " << thread->name << L" (" << thread->native_id << L")"; } - + CASPAR_LOG(info) << "Successfully shutdown CasparCG Server."; if (should_wait_for_keypress) @@ -345,13 +345,11 @@ int main(int argc, char** argv) } catch(const boost::property_tree::file_parser_error& e) { - CASPAR_LOG_CURRENT_EXCEPTION(); CASPAR_LOG(fatal) << "At " << u8(config_file_name) << ":" << e.line() << ": " << e.message() << ". Please check the configuration file (" << u8(config_file_name) << ") for errors."; wait_for_keypress(); } catch (const user_error& e) { - CASPAR_LOG_CURRENT_EXCEPTION_AT_LEVEL(debug); CASPAR_LOG(fatal) << get_message_and_context(e) << " Please check the configuration file (" << u8(config_file_name) << ") for errors. Turn on log level debug for stacktrace."; wait_for_keypress(); }