/* static */ int fx_muxer_t::execute(const int argc, const pal::char_t* argv[]) { trace::verbose(_X("--- Executing in muxer mode...")); if (argc <= 1) { return muxer_usage(); } pal::string_t own_path; // Get the full name of the application if (!pal::get_own_executable_path(&own_path) || !pal::realpath(&own_path)) { trace::error(_X("Failed to locate current executable")); return StatusCode::LibHostCurExeFindFailure; } auto own_dir = get_directory(own_path); bool is_an_app = false; if (pal::strcasecmp(_X("exec"), argv[1]) == 0) { return parse_args_and_execute(own_dir, 2, argc, argv, true, &is_an_app); // arg offset 2 for dotnet, exec } int result = parse_args_and_execute(own_dir, 1, argc, argv, false, &is_an_app); // arg offset 1 for dotnet if (is_an_app) { return result; } // Could not execute as an app, try the CLI SDK dotnet.dll pal::string_t sdk_dotnet; if (!resolve_sdk_dotnet_path(own_dir, &sdk_dotnet)) { trace::error(_X("Could not resolve SDK directory from [%s]"), own_dir.c_str()); return StatusCode::LibHostSdkFindFailure; } append_path(&sdk_dotnet, _X("dotnet.dll")); if (!pal::file_exists(sdk_dotnet)) { trace::error(_X("Could not find dotnet.dll at [%s]"), sdk_dotnet.c_str()); return StatusCode::LibHostSdkFindFailure; } // Transform dotnet [command] [args] -> dotnet dotnet.dll [command] [args] std::vector<const pal::char_t*> new_argv(argc + 1); memcpy(&new_argv.data()[2], argv + 1, (argc - 1) * sizeof(pal::char_t*)); new_argv[0] = argv[0]; new_argv[1] = sdk_dotnet.c_str(); trace::verbose(_X("Using dotnet SDK dll=[%s]"), sdk_dotnet.c_str()); return parse_args_and_execute(own_dir, 1, new_argv.size(), new_argv.data(), false, &is_an_app); }
/* static */ int fx_muxer_t::execute(const int argc, const pal::char_t* argv[]) { pal::string_t own_path; // Get the full name of the application if (!pal::get_own_executable_path(&own_path) || !pal::realpath(&own_path)) { trace::error(_X("Failed to resolve full path of the current executable [%s]"), own_path.c_str()); return StatusCode::LibHostCurExeFindFailure; } pal::string_t own_name = get_filename(own_path); pal::string_t own_dir = get_directory(own_path); pal::string_t own_dll_filename = get_executable(own_name) + _X(".dll"); pal::string_t own_dll = own_dir; append_path(&own_dll, own_dll_filename.c_str()); trace::info(_X("Own DLL path=[%s]"), own_dll.c_str()); auto mode = detect_operating_mode(own_dir, own_dll, own_name); bool is_an_app = true; if (mode == host_mode_t::split_fx) { trace::verbose(_X("--- Executing in split/FX mode...")); return parse_args_and_execute(own_dir, own_dll, 1, argc, argv, false, host_mode_t::split_fx, &is_an_app); } if (mode == host_mode_t::standalone) { trace::verbose(_X("--- Executing in standalone mode...")); return parse_args_and_execute(own_dir, own_dll, 1, argc, argv, false, host_mode_t::standalone, &is_an_app); } trace::verbose(_X("--- Executing in muxer mode...")); if (argc <= 1) { return muxer_usage(); } if (pal::strcasecmp(_X("exec"), argv[1]) == 0) { return parse_args_and_execute(own_dir, own_dll, 2, argc, argv, true, host_mode_t::muxer, &is_an_app); // arg offset 2 for dotnet, exec } int result = parse_args_and_execute(own_dir, own_dll, 1, argc, argv, false, host_mode_t::muxer, &is_an_app); // arg offset 1 for dotnet if (is_an_app) { return result; } // Could not execute as an app, try the CLI SDK dotnet.dll pal::string_t sdk_dotnet; if (!resolve_sdk_dotnet_path(own_dir, &sdk_dotnet)) { assert(argc > 1); if (pal::strcasecmp(_X("--help"), argv[1]) == 0 || pal::strcasecmp(_X("--version"), argv[1]) == 0 || pal::strcasecmp(_X("-h"), argv[1]) == 0 || pal::strcasecmp(_X("-v"), argv[1]) == 0) { return muxer_usage(); } trace::error(_X("Did you mean to run dotnet SDK commands? Please install dotnet SDK from: ")); trace::error(_X(" %s"), s_dotnet_sdk_download_url); return StatusCode::LibHostSdkFindFailure; } append_path(&sdk_dotnet, _X("dotnet.dll")); if (!pal::file_exists(sdk_dotnet)) { trace::error(_X("Found dotnet SDK, but did not find dotnet.dll at [%s]"), sdk_dotnet.c_str()); return StatusCode::LibHostSdkFindFailure; } // Transform dotnet [command] [args] -> dotnet dotnet.dll [command] [args] std::vector<const pal::char_t*> new_argv(argc + 1); memcpy(&new_argv.data()[2], argv + 1, (argc - 1) * sizeof(pal::char_t*)); new_argv[0] = argv[0]; new_argv[1] = sdk_dotnet.c_str(); trace::verbose(_X("Using dotnet SDK dll=[%s]"), sdk_dotnet.c_str()); return parse_args_and_execute(own_dir, own_dll, 1, new_argv.size(), new_argv.data(), false, host_mode_t::muxer, &is_an_app); }
int fx_muxer_t::parse_args_and_execute(const pal::string_t& own_dir, int argoff, int argc, const pal::char_t* argv[], bool exec_mode, bool* is_an_app) { *is_an_app = true; std::vector<pal::string_t> known_opts = { _X("--additionalprobingpath") }; if (exec_mode) { known_opts.push_back(_X("--depsfile")); } // Parse the known muxer arguments if any. int num_parsed = 0; std::unordered_map<pal::string_t, std::vector<pal::string_t>> opts; if (!parse_known_args(argc - argoff, &argv[argoff], known_opts, &opts, &num_parsed)) { trace::error(_X("Failed to parse supported arguments.")); return InvalidArgFailure; } int cur_i = argoff + num_parsed; if (cur_i >= argc) { return muxer_usage(); } pal::string_t app_candidate = argv[cur_i]; bool is_app_runnable = ends_with(app_candidate, _X(".dll"), false) || ends_with(app_candidate, _X(".exe"), false); // If exec mode is on, then check we have a dll at this point if (exec_mode) { if (!is_app_runnable) { trace::error(_X("dotnet exec needs a dll to execute. Try dotnet [--help]")); return InvalidArgFailure; } } // For non-exec, there is CLI invocation or app.dll execution after known args. else { // Test if we have a real dll at this point. if (!is_app_runnable) { // No we don't have a dll, this must be routed to the CLI. *is_an_app = false; return Success; } } // Transform dotnet [exec] [--additionalprobingpath path] [--depsfile file] dll [args] -> dotnet dll [args] std::vector<const pal::char_t*> vec_argv; const pal::char_t** new_argv = argv; int new_argc = argc; if (cur_i != 1) { vec_argv.resize(argc - cur_i + 1, 0); // +1 for dotnet memcpy(vec_argv.data() + 1, argv + cur_i, (argc - cur_i) * sizeof(pal::char_t*)); vec_argv[0] = argv[0]; new_argv = vec_argv.data(); new_argc = vec_argv.size(); } pal::string_t opts_deps_file = _X("--depsfile"); pal::string_t opts_probe_path = _X("--additionalprobingpath"); pal::string_t deps_file = get_last_known_arg(opts, opts_deps_file, _X("")); std::vector<pal::string_t> probe_paths = opts.count(opts_probe_path) ? opts[opts_probe_path] : std::vector<pal::string_t>(); trace::verbose(_X("Current argv is %s"), app_candidate.c_str()); pal::string_t app_or_deps = deps_file.empty() ? app_candidate : deps_file; pal::string_t no_json = app_candidate; pal::string_t dev_config_file; auto config_file = get_runtime_config_from_file(no_json, &dev_config_file); runtime_config_t config(config_file, dev_config_file); for (const auto& path : config.get_probe_paths()) { probe_paths.push_back(path); } if (!config.is_valid()) { trace::error(_X("Invalid runtimeconfig.json [%s] [%s]"), config.get_path().c_str(), config.get_dev_path().c_str()); return StatusCode::InvalidConfigFile; } if (!deps_file.empty() && !pal::file_exists(deps_file)) { trace::error(_X("Deps file [%s] specified but doesn't exist"), deps_file.c_str()); return StatusCode::InvalidArgFailure; } if (config.get_portable()) { trace::verbose(_X("Executing as a portable app as per config file [%s]"), config_file.c_str()); pal::string_t fx_dir = resolve_fx_dir(own_dir, &config); corehost_init_t init(deps_file, probe_paths, fx_dir, host_mode_t::muxer, &config); return execute_app(fx_dir, &init, new_argc, new_argv); } else { trace::verbose(_X("Executing as a standalone app as per config file [%s]"), config_file.c_str()); pal::string_t impl_dir = get_directory(app_or_deps); if (!library_exists_in_dir(impl_dir, LIBHOSTPOLICY_NAME, nullptr) && !probe_paths.empty() && !deps_file.empty()) { bool found = false; pal::string_t candidate = impl_dir; deps_json_t deps_json(false, deps_file); for (const auto& probe_path : probe_paths) { trace::verbose(_X("Considering %s for hostpolicy library"), probe_path.c_str()); if (deps_json.is_valid() && deps_json.has_hostpolicy_entry() && deps_json.get_hostpolicy_entry().to_full_path(probe_path, &candidate)) { found = true; // candidate contains the right path. break; } } if (!found) { trace::error(_X("Policy library either not found in deps [%s] or not found in %d probe paths."), deps_file.c_str(), probe_paths.size()); return StatusCode::CoreHostLibMissingFailure; } impl_dir = get_directory(candidate); } corehost_init_t init(deps_file, probe_paths, _X(""), host_mode_t::muxer, &config); return execute_app(impl_dir, &init, new_argc, new_argv); } }
int fx_muxer_t::parse_args_and_execute( const pal::string_t& own_dir, const pal::string_t& own_dll, int argoff, int argc, const pal::char_t* argv[], bool exec_mode, host_mode_t mode, bool* is_an_app) { *is_an_app = true; std::vector<pal::string_t> known_opts = { _X("--additionalprobingpath") }; if (exec_mode || mode == host_mode_t::split_fx || mode == host_mode_t::standalone) { known_opts.push_back(_X("--depsfile")); known_opts.push_back(_X("--runtimeconfig")); } if (mode == host_mode_t::muxer) { known_opts.push_back(_X("--fx-version")); } // Parse the known arguments if any. int num_parsed = 0; std::unordered_map<pal::string_t, std::vector<pal::string_t>> opts; if (!parse_known_args(argc - argoff, &argv[argoff], known_opts, &opts, &num_parsed)) { trace::error(_X("Failed to parse supported options or their values:")); for (const auto& arg : known_opts) { trace::error(_X(" %s"), arg.c_str()); } return InvalidArgFailure; } const pal::char_t** new_argv = argv; int new_argc = argc; std::vector<const pal::char_t*> vec_argv; pal::string_t app_candidate = own_dll; int cur_i = argoff + num_parsed; if (mode != host_mode_t::standalone) { trace::verbose(_X("Detected a non-standalone application, expecting app.dll to execute.")); if (cur_i >= argc) { return muxer_usage(); } app_candidate = argv[cur_i]; bool is_app_managed = (ends_with(app_candidate, _X(".dll"), false) || ends_with(app_candidate, _X(".exe"), false)) && pal::realpath(&app_candidate); if (!is_app_managed) { trace::verbose(_X("Application '%s' is not a managed executable."), app_candidate.c_str()); *is_an_app = false; if (exec_mode) { trace::error(_X("dotnet exec needs a managed .dll or .exe extension. The application specified was '%s'"), app_candidate.c_str()); return InvalidArgFailure; } // Route to CLI. return AppArgNotRunnable; } } // App is managed executable. trace::verbose(_X("Treating application '%s' as a managed executable."), app_candidate.c_str()); if (!pal::file_exists(app_candidate)) { trace::error(_X("The application to execute does not exist: '%s'"), app_candidate.c_str()); return InvalidArgFailure; } if (cur_i != 1) { vec_argv.resize(argc - cur_i + 1, 0); // +1 for dotnet memcpy(vec_argv.data() + 1, argv + cur_i, (argc - cur_i) * sizeof(pal::char_t*)); vec_argv[0] = argv[0]; new_argv = vec_argv.data(); new_argc = vec_argv.size(); } // Transform dotnet [exec] [--additionalprobingpath path] [--depsfile file] [dll] [args] -> dotnet [dll] [args] return read_config_and_execute(own_dir, app_candidate, opts, new_argc, new_argv, mode); }