From b12172a79c93511d2e17362be19c490d7fd797ee Mon Sep 17 00:00:00 2001 From: Phil Grunzinger Date: Sun, 12 Feb 2023 08:23:40 -0600 Subject: [PATCH 01/14] Add support for global prep commands --- src/confighttp.cpp | 40 +++ src/process.cpp | 178 +++++++--- src/process.h | 17 +- src_assets/common/assets/web/apps.html | 469 +++++++++++++++---------- 4 files changed, 477 insertions(+), 227 deletions(-) diff --git a/src/confighttp.cpp b/src/confighttp.cpp index 5f54aebb8fb..b47abba7c1e 100644 --- a/src/confighttp.cpp +++ b/src/confighttp.cpp @@ -489,6 +489,45 @@ void uploadCover(resp_https_t response, req_https_t request) { outputTree.put("path", path); } +void savePrepCmd(resp_https_t response, req_https_t request) { + if(!authenticate(response, request)) return; + + print_req(request); + + std::stringstream ss; + ss << request->content.rdbuf(); + + pt::ptree outputTree; + auto g = util::fail_guard([&]() { + std::ostringstream data; + + pt::write_json(data, outputTree); + response->write(data.str()); + }); + + pt::ptree inputTree, fileTree; + + BOOST_LOG(fatal) << config::stream.file_apps; + try { + pt::read_json(ss, inputTree); + pt::read_json(config::stream.file_apps, fileTree); + fileTree.erase("global-prep-cmd"); + fileTree.push_back(std::make_pair("global-prep-cmd", inputTree)); + pt::write_json(config::stream.file_apps, fileTree); + } + catch(std::exception &e) { + BOOST_LOG(warning) << "saveGlobalPrepCmd: "sv << e.what(); + + outputTree.put("status", "false"); + outputTree.put("error", "Invalid Input JSON"); + return; + } + + outputTree.put("status", "true"); + proc::refresh(config::stream.file_apps); +} + + void getConfig(resp_https_t response, req_https_t request) { if(!authenticate(response, request)) return; @@ -719,6 +758,7 @@ void start() { server.resource["^/api/apps$"]["GET"] = getApps; server.resource["^/api/logs$"]["GET"] = getLogs; server.resource["^/api/apps$"]["POST"] = saveApp; + server.resource["^/api/apps/prep$"]["POST"] = savePrepCmd; server.resource["^/api/config$"]["GET"] = getConfig; server.resource["^/api/config$"]["POST"] = saveConfig; server.resource["^/api/restart$"]["POST"] = restart; diff --git a/src/process.cpp b/src/process.cpp index 9a6c9253873..d5558c97ce8 100644 --- a/src/process.cpp +++ b/src/process.cpp @@ -49,14 +49,6 @@ void process_end(bp::child &proc, bp::group &proc_handle) { proc.wait(); } -int exe_with_full_privs(const std::string &cmd, bp::environment &env, file_t &file, std::error_code &ec) { - if(!file) { - return bp::system(cmd, env, bp::std_out > bp::null, bp::std_err > bp::null, ec); - } - - return bp::system(cmd, env, bp::std_out > file.get(), bp::std_err > file.get(), ec); -} - boost::filesystem::path find_working_directory(const std::string &cmd, bp::environment &env) { // Parse the raw command string into parts to get the actual command portion #ifdef _WIN32 @@ -88,7 +80,7 @@ boost::filesystem::path find_working_directory(const std::string &cmd, bp::envir } int proc_t::execute(int app_id) { - // Ensure starting from a clean slate + // Ensure starting from a clean slate. terminate(); auto iter = std::find_if(_apps.begin(), _apps.end(), [&app_id](const auto app) { @@ -100,25 +92,28 @@ int proc_t::execute(int app_id) { return 404; } - _app_id = app_id; - auto &proc = *iter; + _app_id = app_id; + _app = *iter; + + _app_prep_begin = std::begin(_app.prep_cmds); + _app_prep_it = _app_prep_begin; - _undo_begin = std::begin(proc.prep_cmds); - _undo_it = _undo_begin; + _global_prep_begin = std::begin(_prep_cmds); + _global_prep_it = _global_prep_begin; - if(!proc.output.empty() && proc.output != "null"sv) { + if(!_app.output.empty() && _app.output != "null"sv) { #ifdef _WIN32 // fopen() interprets the filename as an ANSI string on Windows, so we must convert it // to UTF-16 and use the wchar_t variants for proper Unicode log file path support. std::wstring_convert, wchar_t> converter; - auto woutput = converter.from_bytes(proc.output); + auto woutput = converter.from_bytes(_app.output); // Use _SH_DENYNO to allow us to open this log file again for writing even if it is // still open from a previous execution. This is required to handle the case of a // detached process executing again while the previous process is still running. _pipe.reset(_wfsopen(woutput.c_str(), L"a", _SH_DENYNO)); #else - _pipe.reset(fopen(proc.output.c_str(), "a")); + _pipe.reset(fopen(_app.output.c_str(), "a")); #endif } @@ -128,27 +123,61 @@ int proc_t::execute(int app_id) { terminate(); }); - for(; _undo_it != std::end(proc.prep_cmds); ++_undo_it) { - auto &cmd = _undo_it->do_cmd; + // Execute global prep commands if enabled + if (_app.include_global_prep) + { + for(; _global_prep_it != std::end(_prep_cmds); ++_global_prep_it) { + auto &cmd = _global_prep_it->do_cmd; + + boost::filesystem::path working_dir = _app.working_dir.empty() ? + find_working_directory(cmd, _env) : + boost::filesystem::path(_app.working_dir); + BOOST_LOG(info) << "Executing Global Do Cmd: ["sv << cmd << ']'; + auto child = platf::run_unprivileged(cmd, working_dir, _env, _pipe.get(), ec, nullptr); + + if(ec) { + BOOST_LOG(error) << "Couldn't run ["sv << cmd << "]: System: "sv << ec.message(); + return -1; + } + + child.wait(); + auto ret = child.exit_code(); + + if(ret != 0) { + BOOST_LOG(error) << '[' << cmd << "] failed with code ["sv << ret << ']'; + return -1; + } + } + } + + // Execute app prep commands + for(; _app_prep_it != std::end(_app.prep_cmds); ++_app_prep_it) { + auto &cmd = _app_prep_it->do_cmd; - BOOST_LOG(info) << "Executing: ["sv << cmd << ']'; - auto ret = exe_with_full_privs(cmd, _env, _pipe, ec); + boost::filesystem::path working_dir = _app.working_dir.empty() ? + find_working_directory(cmd, _env) : + boost::filesystem::path(_app.working_dir); + BOOST_LOG(info) << "Executing Do Cmd: ["sv << cmd << ']'; + auto child = platf::run_unprivileged(cmd, working_dir, _env, _pipe.get(), ec, nullptr); if(ec) { BOOST_LOG(error) << "Couldn't run ["sv << cmd << "]: System: "sv << ec.message(); return -1; } + child.wait(); + auto ret = child.exit_code(); + if(ret != 0) { BOOST_LOG(error) << '[' << cmd << "] failed with code ["sv << ret << ']'; return -1; } } - for(auto &cmd : proc.detached) { - boost::filesystem::path working_dir = proc.working_dir.empty() ? + for(auto &cmd : _app.detached) { + boost::filesystem::path working_dir = _app.working_dir.empty() ? find_working_directory(cmd, _env) : - boost::filesystem::path(proc.working_dir); + boost::filesystem::path(_app.working_dir); BOOST_LOG(info) << "Spawning ["sv << cmd << "] in ["sv << working_dir << ']'; auto child = platf::run_unprivileged(cmd, working_dir, _env, _pipe.get(), ec, nullptr); if(ec) { @@ -159,18 +188,18 @@ int proc_t::execute(int app_id) { } } - if(proc.cmd.empty()) { + if(_app.cmd.empty()) { BOOST_LOG(info) << "Executing [Desktop]"sv; placebo = true; } else { - boost::filesystem::path working_dir = proc.working_dir.empty() ? - find_working_directory(proc.cmd, _env) : - boost::filesystem::path(proc.working_dir); - BOOST_LOG(info) << "Executing: ["sv << proc.cmd << "] in ["sv << working_dir << ']'; - _process = platf::run_unprivileged(proc.cmd, working_dir, _env, _pipe.get(), ec, &_process_handle); + boost::filesystem::path working_dir = _app.working_dir.empty() ? + find_working_directory(_app.cmd, _env) : + boost::filesystem::path(_app.working_dir); + BOOST_LOG(info) << "Executing: ["sv << _app.cmd << "] in ["sv << working_dir << ']'; + _process = platf::run_unprivileged(_app.cmd, working_dir, _env, _pipe.get(), ec, &_process_handle); if(ec) { - BOOST_LOG(warning) << "Couldn't run ["sv << proc.cmd << "]: System: "sv << ec.message(); + BOOST_LOG(warning) << "Couldn't run ["sv << _app.cmd << "]: System: "sv << ec.message(); return -1; } } @@ -203,25 +232,59 @@ void proc_t::terminate() { _process_handle = bp::group(); _app_id = -1; - for(; _undo_it != _undo_begin; --_undo_it) { - auto &cmd = (_undo_it - 1)->undo_cmd; + for(; _app_prep_it != _app_prep_begin; --_app_prep_it) { + auto &cmd = (_app_prep_it - 1)->undo_cmd; if(cmd.empty()) { continue; } - BOOST_LOG(info) << "Executing: ["sv << cmd << ']'; - - auto ret = exe_with_full_privs(cmd, _env, _pipe, ec); + boost::filesystem::path working_dir = _app.working_dir.empty() ? + find_working_directory(cmd, _env) : + boost::filesystem::path(_app.working_dir); + BOOST_LOG(info) << "Executing Undo Cmd: ["sv << cmd << ']'; + auto child = platf::run_unprivileged(cmd, working_dir, _env, _pipe.get(), ec, nullptr); if(ec) { BOOST_LOG(warning) << "System: "sv << ec.message(); } + + child.wait(); + auto ret = child.exit_code(); if(ret != 0) { BOOST_LOG(warning) << "Return code ["sv << ret << ']'; } } + + // Execute global prep commands if enabled + if (_app.include_global_prep) + { + for(; _global_prep_it != _global_prep_begin; --_global_prep_it) { + auto &cmd = (_global_prep_it - 1)->undo_cmd; + + if(cmd.empty()) { + continue; + } + + boost::filesystem::path working_dir = _app.working_dir.empty() ? + find_working_directory(cmd, _env) : + boost::filesystem::path(_app.working_dir); + BOOST_LOG(info) << "Executing Global Undo Cmd: ["sv << cmd << ']'; + auto child = platf::run_unprivileged(cmd, working_dir, _env, _pipe.get(), ec, nullptr); + + if(ec) { + BOOST_LOG(warning) << "System: "sv << ec.message(); + } + + child.wait(); + auto ret = child.exit_code(); + + if(ret != 0) { + BOOST_LOG(warning) << "Return code ["sv << ret << ']'; + } + } + } _pipe.reset(); } @@ -439,8 +502,9 @@ std::optional parse(const std::string &file_name) { try { pt::read_json(file_name, tree); - auto &apps_node = tree.get_child("apps"s); - auto &env_vars = tree.get_child("env"s); + auto &apps_node = tree.get_child("apps"s); + auto &env_vars = tree.get_child("env"s); + auto global_prep_nodes_opt = tree.get_child_optional("global-prep-cmd"s); auto this_env = boost::this_process::environment(); @@ -448,19 +512,39 @@ std::optional parse(const std::string &file_name) { this_env[name] = parse_env_val(this_env, val.get_value()); } + + std::vector global_prep_cmds; + if(global_prep_nodes_opt) { + auto &global_prep_nodes = *global_prep_nodes_opt; + + global_prep_cmds.reserve(global_prep_nodes.size()); + for(auto &[_, prep_node] : global_prep_nodes) { + auto do_cmd = parse_env_val(this_env, prep_node.get("do"s)); + auto undo_cmd = prep_node.get_optional("undo"s); + + if(undo_cmd) { + global_prep_cmds.emplace_back(std::move(do_cmd), parse_env_val(this_env, *undo_cmd)); + } + else { + global_prep_cmds.emplace_back(std::move(do_cmd)); + } + } + } + std::set ids; std::vector apps; int i = 0; for(auto &[_, app_node] : apps_node) { proc::ctx_t ctx; - auto prep_nodes_opt = app_node.get_child_optional("prep-cmd"s); - auto detached_nodes_opt = app_node.get_child_optional("detached"s); - auto output = app_node.get_optional("output"s); - auto name = parse_env_val(this_env, app_node.get("name"s)); - auto cmd = app_node.get_optional("cmd"s); - auto image_path = app_node.get_optional("image-path"s); - auto working_dir = app_node.get_optional("working-dir"s); + auto prep_nodes_opt = app_node.get_child_optional("prep-cmd"s); + auto detached_nodes_opt = app_node.get_child_optional("detached"s); + auto include_global_prep = app_node.get_optional("include-global-prep-cmd"s); + auto output = app_node.get_optional("output"s); + auto name = parse_env_val(this_env, app_node.get("name"s)); + auto cmd = app_node.get_optional("cmd"s); + auto image_path = app_node.get_optional("image-path"s); + auto working_dir = app_node.get_optional("working-dir"s); std::vector prep_cmds; if(prep_nodes_opt) { @@ -506,6 +590,10 @@ std::optional parse(const std::string &file_name) { ctx.image_path = parse_env_val(this_env, *image_path); } + if(include_global_prep) { + ctx.include_global_prep = *include_global_prep; + } + auto possible_ids = calculate_app_id(name, ctx.image_path, i++); if(ids.count(std::get<0>(possible_ids)) == 0) { // Avoid using index to generate id if possible @@ -525,7 +613,7 @@ std::optional parse(const std::string &file_name) { } return proc::proc_t { - std::move(this_env), std::move(apps) + std::move(this_env), std::move(apps), std::move(global_prep_cmds) }; } catch(std::exception &e) { diff --git a/src/process.h b/src/process.h index e4bd0170e37..9b019d5d3e1 100644 --- a/src/process.h +++ b/src/process.h @@ -39,6 +39,8 @@ struct cmd_t { * filename -- The output of the commands are appended to filename */ struct ctx_t { + ctx_t() : include_global_prep(true) {} + std::vector prep_cmds; /** @@ -55,6 +57,7 @@ struct ctx_t { std::string output; std::string image_path; std::string id; + bool include_global_prep; }; class proc_t { @@ -63,9 +66,11 @@ class proc_t { proc_t( boost::process::environment &&env, - std::vector &&apps) : _app_id(0), + std::vector &&apps, + std::vector &&prep_cmds) : _app_id(0), _env(std::move(env)), - _apps(std::move(apps)) {} + _apps(std::move(apps)), + _prep_cmds(std::move(prep_cmds)) {} int execute(int app_id); @@ -87,6 +92,8 @@ class proc_t { boost::process::environment _env; std::vector _apps; + ctx_t _app; + std::vector _prep_cmds; // If no command associated with _app_id, yet it's still running bool placebo {}; @@ -95,8 +102,10 @@ class proc_t { boost::process::group _process_handle; file_t _pipe; - std::vector::const_iterator _undo_it; - std::vector::const_iterator _undo_begin; + std::vector::const_iterator _app_prep_it; + std::vector::const_iterator _app_prep_begin; + std::vector::const_iterator _global_prep_it; + std::vector::const_iterator _global_prep_begin; }; /** diff --git a/src_assets/common/assets/web/apps.html b/src_assets/common/assets/web/apps.html index 87c22c0d0fe..8d547522bef 100644 --- a/src_assets/common/assets/web/apps.html +++ b/src_assets/common/assets/web/apps.html @@ -3,73 +3,271 @@

Applications

Applications are refreshed only when Client is restarted
-
- - - - - - - - - - - - - -
NameActions
{{app.name}} - - -
-
-
-
- -
- - -
- Application Name, as shown on Moonlight +
+ +
+ +
+
{{c}}
+ +
+
+ + +
+
+ A list of commands to be run and forgotten about +
+
+ +
+ + +
+ The main application, if it is not specified, a processs is started + that sleeps indefinitely +
+
+ +
+ + +
+ The working directory that should be passed to the process. + For example, some applications use the working directory to search for configuration files. + If not set, Sunshine will default to the parent directory of the command +
+
+ +
+ +
+ + + +
+
+ Application icon/picture/image path that will be sent to client. Image must be a PNG file. + If not set, Sunshine will send default box image. +
+
+ +
+ + +
- -
- - -
- The file where the output of the command is stored, if it is not - specified, the output is ignored -
+
+
- +
+ +
- +
- A list of commands to be run before/after the application.
- If any of the prep-commands fail, starting the application is aborted + A list of commands to be run before/after all applications.
+ If any of the prep-commands fail, starting the application is aborted.
- +
- +
Do Undo
Applications @@ -98,135 +296,14 @@

Applications

- -
- -
-
{{c}}
- -
-
- - -
-
- A list of commands to be run and forgotten about -
-
- -
- - -
- The main application, if it is not specified, a processs is started - that sleeps indefinitely -
-
- -
- - -
- The working directory that should be passed to the process. - For example, some applications use the working directory to search for configuration files. - If not set, Sunshine will default to the parent directory of the command -
-
- -
- -
- - - -
-
- Application icon/picture/image path that will be sent to client. Image must be a PNG file. - If not set, Sunshine will send default box image. -
-
- -
- - -
+ -
- -
@@ -438,4 +545,10 @@ object-fit: cover; } + .config-page { + padding: 1em; + border: 1px solid #dee2e6; + border-top: none; + } + \ No newline at end of file From 34c4e5fff974e5e4d453bbc5818114dc899b4c26 Mon Sep 17 00:00:00 2001 From: pgrunzjr Date: Mon, 6 Mar 2023 22:30:01 -0600 Subject: [PATCH 02/14] Invert global prep exclusion --- src/process.cpp | 10 +++++----- src/process.h | 4 +--- src_assets/common/assets/web/apps.html | 12 ++++++------ 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/process.cpp b/src/process.cpp index d5558c97ce8..aa3587f48a7 100644 --- a/src/process.cpp +++ b/src/process.cpp @@ -124,7 +124,7 @@ int proc_t::execute(int app_id) { }); // Execute global prep commands if enabled - if (_app.include_global_prep) + if (!_app.exclude_global_prep) { for(; _global_prep_it != std::end(_prep_cmds); ++_global_prep_it) { auto &cmd = _global_prep_it->do_cmd; @@ -258,7 +258,7 @@ void proc_t::terminate() { } // Execute global prep commands if enabled - if (_app.include_global_prep) + if (!_app.exclude_global_prep) { for(; _global_prep_it != _global_prep_begin; --_global_prep_it) { auto &cmd = (_global_prep_it - 1)->undo_cmd; @@ -539,7 +539,7 @@ std::optional parse(const std::string &file_name) { auto prep_nodes_opt = app_node.get_child_optional("prep-cmd"s); auto detached_nodes_opt = app_node.get_child_optional("detached"s); - auto include_global_prep = app_node.get_optional("include-global-prep-cmd"s); + auto exclude_global_prep = app_node.get_optional("exclude-global-prep-cmd"s); auto output = app_node.get_optional("output"s); auto name = parse_env_val(this_env, app_node.get("name"s)); auto cmd = app_node.get_optional("cmd"s); @@ -590,8 +590,8 @@ std::optional parse(const std::string &file_name) { ctx.image_path = parse_env_val(this_env, *image_path); } - if(include_global_prep) { - ctx.include_global_prep = *include_global_prep; + if(exclude_global_prep) { + ctx.exclude_global_prep = *exclude_global_prep; } auto possible_ids = calculate_app_id(name, ctx.image_path, i++); diff --git a/src/process.h b/src/process.h index 9b019d5d3e1..d2a263d15a5 100644 --- a/src/process.h +++ b/src/process.h @@ -39,8 +39,6 @@ struct cmd_t { * filename -- The output of the commands are appended to filename */ struct ctx_t { - ctx_t() : include_global_prep(true) {} - std::vector prep_cmds; /** @@ -57,7 +55,7 @@ struct ctx_t { std::string output; std::string image_path; std::string id; - bool include_global_prep; + bool exclude_global_prep; }; class proc_t { diff --git a/src_assets/common/assets/web/apps.html b/src_assets/common/assets/web/apps.html index 8d547522bef..1fc8e333958 100644 --- a/src_assets/common/assets/web/apps.html +++ b/src_assets/common/assets/web/apps.html @@ -73,9 +73,9 @@

Applications

- - +
Enable/Disable the execution of Global Prep Commands for this application. @@ -354,7 +354,7 @@ output: "", cmd: [], index: -1, - "include-global-prep-cmd": true, + "exclude-global-prep-cmd": false, "prep-cmd": [], detached: [], "image-path": "" @@ -369,8 +369,8 @@ this.$set(this.editForm, "prep-cmd", []); if (this.editForm["detached"] === undefined) this.$set(this.editForm, "detached", []); - if (this.editForm["include-global-prep-cmd"] === undefined) - this.$set(this.editForm, "include-global-prep-cmd", true); + if (this.editForm["exclude-global-prep-cmd"] === undefined) + this.$set(this.editForm, "exclude-global-prep-cmd", false); this.showEditForm = true; }, showDeleteForm(id) { From 00cc2a2202d0869bd20229129f2a97d63bb72adc Mon Sep 17 00:00:00 2001 From: pgrunzjr Date: Mon, 6 Mar 2023 23:45:51 -0600 Subject: [PATCH 03/14] Combine global commands with app commands on load --- src/process.cpp | 97 ++++++++++--------------------------------------- src/process.h | 9 +---- 2 files changed, 21 insertions(+), 85 deletions(-) diff --git a/src/process.cpp b/src/process.cpp index aa3587f48a7..7aadb1a7f0f 100644 --- a/src/process.cpp +++ b/src/process.cpp @@ -98,9 +98,6 @@ int proc_t::execute(int app_id) { _app_prep_begin = std::begin(_app.prep_cmds); _app_prep_it = _app_prep_begin; - _global_prep_begin = std::begin(_prep_cmds); - _global_prep_it = _global_prep_begin; - if(!_app.output.empty() && _app.output != "null"sv) { #ifdef _WIN32 // fopen() interprets the filename as an ANSI string on Windows, so we must convert it @@ -123,33 +120,6 @@ int proc_t::execute(int app_id) { terminate(); }); - // Execute global prep commands if enabled - if (!_app.exclude_global_prep) - { - for(; _global_prep_it != std::end(_prep_cmds); ++_global_prep_it) { - auto &cmd = _global_prep_it->do_cmd; - - boost::filesystem::path working_dir = _app.working_dir.empty() ? - find_working_directory(cmd, _env) : - boost::filesystem::path(_app.working_dir); - BOOST_LOG(info) << "Executing Global Do Cmd: ["sv << cmd << ']'; - auto child = platf::run_unprivileged(cmd, working_dir, _env, _pipe.get(), ec, nullptr); - - if(ec) { - BOOST_LOG(error) << "Couldn't run ["sv << cmd << "]: System: "sv << ec.message(); - return -1; - } - - child.wait(); - auto ret = child.exit_code(); - - if(ret != 0) { - BOOST_LOG(error) << '[' << cmd << "] failed with code ["sv << ret << ']'; - return -1; - } - } - } - // Execute app prep commands for(; _app_prep_it != std::end(_app.prep_cmds); ++_app_prep_it) { auto &cmd = _app_prep_it->do_cmd; @@ -256,35 +226,6 @@ void proc_t::terminate() { BOOST_LOG(warning) << "Return code ["sv << ret << ']'; } } - - // Execute global prep commands if enabled - if (!_app.exclude_global_prep) - { - for(; _global_prep_it != _global_prep_begin; --_global_prep_it) { - auto &cmd = (_global_prep_it - 1)->undo_cmd; - - if(cmd.empty()) { - continue; - } - - boost::filesystem::path working_dir = _app.working_dir.empty() ? - find_working_directory(cmd, _env) : - boost::filesystem::path(_app.working_dir); - BOOST_LOG(info) << "Executing Global Undo Cmd: ["sv << cmd << ']'; - auto child = platf::run_unprivileged(cmd, working_dir, _env, _pipe.get(), ec, nullptr); - - if(ec) { - BOOST_LOG(warning) << "System: "sv << ec.message(); - } - - child.wait(); - auto ret = child.exit_code(); - - if(ret != 0) { - BOOST_LOG(warning) << "Return code ["sv << ret << ']'; - } - } - } _pipe.reset(); } @@ -513,23 +454,6 @@ std::optional parse(const std::string &file_name) { } - std::vector global_prep_cmds; - if(global_prep_nodes_opt) { - auto &global_prep_nodes = *global_prep_nodes_opt; - - global_prep_cmds.reserve(global_prep_nodes.size()); - for(auto &[_, prep_node] : global_prep_nodes) { - auto do_cmd = parse_env_val(this_env, prep_node.get("do"s)); - auto undo_cmd = prep_node.get_optional("undo"s); - - if(undo_cmd) { - global_prep_cmds.emplace_back(std::move(do_cmd), parse_env_val(this_env, *undo_cmd)); - } - else { - global_prep_cmds.emplace_back(std::move(do_cmd)); - } - } - } std::set ids; std::vector apps; @@ -547,10 +471,27 @@ std::optional parse(const std::string &file_name) { auto working_dir = app_node.get_optional("working-dir"s); std::vector prep_cmds; + if(global_prep_nodes_opt && !exclude_global_prep.value_or(false)) { + auto &global_prep_nodes = *global_prep_nodes_opt; + + prep_cmds.reserve(global_prep_nodes.size()); + for(auto &[_, prep_node] : global_prep_nodes) { + auto do_cmd = parse_env_val(this_env, prep_node.get("do"s)); + auto undo_cmd = prep_node.get_optional("undo"s); + + if(undo_cmd) { + prep_cmds.emplace_back(std::move(do_cmd), parse_env_val(this_env, *undo_cmd)); + } + else { + prep_cmds.emplace_back(std::move(do_cmd)); + } + } + } + if(prep_nodes_opt) { auto &prep_nodes = *prep_nodes_opt; - prep_cmds.reserve(prep_nodes.size()); + prep_cmds.reserve(prep_cmds.size() + prep_nodes.size()); for(auto &[_, prep_node] : prep_nodes) { auto do_cmd = parse_env_val(this_env, prep_node.get("do"s)); auto undo_cmd = prep_node.get_optional("undo"s); @@ -613,7 +554,7 @@ std::optional parse(const std::string &file_name) { } return proc::proc_t { - std::move(this_env), std::move(apps), std::move(global_prep_cmds) + std::move(this_env), std::move(apps) }; } catch(std::exception &e) { diff --git a/src/process.h b/src/process.h index d2a263d15a5..6f9b549594e 100644 --- a/src/process.h +++ b/src/process.h @@ -64,11 +64,9 @@ class proc_t { proc_t( boost::process::environment &&env, - std::vector &&apps, - std::vector &&prep_cmds) : _app_id(0), + std::vector &&apps) : _app_id(0), _env(std::move(env)), - _apps(std::move(apps)), - _prep_cmds(std::move(prep_cmds)) {} + _apps(std::move(apps)) {} int execute(int app_id); @@ -91,7 +89,6 @@ class proc_t { boost::process::environment _env; std::vector _apps; ctx_t _app; - std::vector _prep_cmds; // If no command associated with _app_id, yet it's still running bool placebo {}; @@ -102,8 +99,6 @@ class proc_t { file_t _pipe; std::vector::const_iterator _app_prep_it; std::vector::const_iterator _app_prep_begin; - std::vector::const_iterator _global_prep_it; - std::vector::const_iterator _global_prep_begin; }; /** From 07c116c85e47e9b90571cc884eb146dea19f2681 Mon Sep 17 00:00:00 2001 From: pgrunzjr Date: Mon, 6 Mar 2023 23:56:17 -0600 Subject: [PATCH 04/14] Remove now unused exclude_global_prep member from ctx_t --- src/process.cpp | 4 ---- src/process.h | 1 - 2 files changed, 5 deletions(-) diff --git a/src/process.cpp b/src/process.cpp index 7aadb1a7f0f..19ae76aec0b 100644 --- a/src/process.cpp +++ b/src/process.cpp @@ -531,10 +531,6 @@ std::optional parse(const std::string &file_name) { ctx.image_path = parse_env_val(this_env, *image_path); } - if(exclude_global_prep) { - ctx.exclude_global_prep = *exclude_global_prep; - } - auto possible_ids = calculate_app_id(name, ctx.image_path, i++); if(ids.count(std::get<0>(possible_ids)) == 0) { // Avoid using index to generate id if possible diff --git a/src/process.h b/src/process.h index 6f9b549594e..99af563d39f 100644 --- a/src/process.h +++ b/src/process.h @@ -55,7 +55,6 @@ struct ctx_t { std::string output; std::string image_path; std::string id; - bool exclude_global_prep; }; class proc_t { From c27cd615cc8961cdb85f1edbe9cc44e25fba0fb7 Mon Sep 17 00:00:00 2001 From: pgrunzjr Date: Thu, 9 Mar 2023 14:26:11 -0600 Subject: [PATCH 05/14] Rememove elevation changes --- src/process.cpp | 76 +++++++++++++++++++++++-------------------------- src/process.h | 5 ++-- 2 files changed, 37 insertions(+), 44 deletions(-) diff --git a/src/process.cpp b/src/process.cpp index 19ae76aec0b..63f3ec28a6d 100644 --- a/src/process.cpp +++ b/src/process.cpp @@ -49,6 +49,14 @@ void process_end(bp::child &proc, bp::group &proc_handle) { proc.wait(); } +int exe_with_full_privs(const std::string &cmd, bp::environment &env, file_t &file, std::error_code &ec) { + if(!file) { + return bp::system(cmd, env, bp::std_out > bp::null, bp::std_err > bp::null, ec); + } + + return bp::system(cmd, env, bp::std_out > file.get(), bp::std_err > file.get(), ec); +} + boost::filesystem::path find_working_directory(const std::string &cmd, bp::environment &env) { // Parse the raw command string into parts to get the actual command portion #ifdef _WIN32 @@ -80,7 +88,7 @@ boost::filesystem::path find_working_directory(const std::string &cmd, bp::envir } int proc_t::execute(int app_id) { - // Ensure starting from a clean slate. + // Ensure starting from a clean slate terminate(); auto iter = std::find_if(_apps.begin(), _apps.end(), [&app_id](const auto app) { @@ -92,25 +100,25 @@ int proc_t::execute(int app_id) { return 404; } - _app_id = app_id; - _app = *iter; + _app_id = app_id; + auto &proc = *iter; - _app_prep_begin = std::begin(_app.prep_cmds); - _app_prep_it = _app_prep_begin; + _undo_begin = std::begin(proc.prep_cmds); + _undo_it = _undo_begin; - if(!_app.output.empty() && _app.output != "null"sv) { + if(!proc.output.empty() && proc.output != "null"sv) { #ifdef _WIN32 // fopen() interprets the filename as an ANSI string on Windows, so we must convert it // to UTF-16 and use the wchar_t variants for proper Unicode log file path support. std::wstring_convert, wchar_t> converter; - auto woutput = converter.from_bytes(_app.output); + auto woutput = converter.from_bytes(proc.output); // Use _SH_DENYNO to allow us to open this log file again for writing even if it is // still open from a previous execution. This is required to handle the case of a // detached process executing again while the previous process is still running. _pipe.reset(_wfsopen(woutput.c_str(), L"a", _SH_DENYNO)); #else - _pipe.reset(fopen(_app.output.c_str(), "a")); + _pipe.reset(fopen(proc.output.c_str(), "a")); #endif } @@ -120,34 +128,27 @@ int proc_t::execute(int app_id) { terminate(); }); - // Execute app prep commands - for(; _app_prep_it != std::end(_app.prep_cmds); ++_app_prep_it) { - auto &cmd = _app_prep_it->do_cmd; + for(; _undo_it != std::end(proc.prep_cmds); ++_undo_it) { + auto &cmd = _undo_it->do_cmd; - boost::filesystem::path working_dir = _app.working_dir.empty() ? - find_working_directory(cmd, _env) : - boost::filesystem::path(_app.working_dir); - BOOST_LOG(info) << "Executing Do Cmd: ["sv << cmd << ']'; - auto child = platf::run_unprivileged(cmd, working_dir, _env, _pipe.get(), ec, nullptr); + BOOST_LOG(info) << "Executing: ["sv << cmd << ']'; + auto ret = exe_with_full_privs(cmd, _env, _pipe, ec); if(ec) { BOOST_LOG(error) << "Couldn't run ["sv << cmd << "]: System: "sv << ec.message(); return -1; } - child.wait(); - auto ret = child.exit_code(); - if(ret != 0) { BOOST_LOG(error) << '[' << cmd << "] failed with code ["sv << ret << ']'; return -1; } } - for(auto &cmd : _app.detached) { - boost::filesystem::path working_dir = _app.working_dir.empty() ? + for(auto &cmd : proc.detached) { + boost::filesystem::path working_dir = proc.working_dir.empty() ? find_working_directory(cmd, _env) : - boost::filesystem::path(_app.working_dir); + boost::filesystem::path(proc.working_dir); BOOST_LOG(info) << "Spawning ["sv << cmd << "] in ["sv << working_dir << ']'; auto child = platf::run_unprivileged(cmd, working_dir, _env, _pipe.get(), ec, nullptr); if(ec) { @@ -158,18 +159,18 @@ int proc_t::execute(int app_id) { } } - if(_app.cmd.empty()) { + if(proc.cmd.empty()) { BOOST_LOG(info) << "Executing [Desktop]"sv; placebo = true; } else { - boost::filesystem::path working_dir = _app.working_dir.empty() ? - find_working_directory(_app.cmd, _env) : - boost::filesystem::path(_app.working_dir); - BOOST_LOG(info) << "Executing: ["sv << _app.cmd << "] in ["sv << working_dir << ']'; - _process = platf::run_unprivileged(_app.cmd, working_dir, _env, _pipe.get(), ec, &_process_handle); + boost::filesystem::path working_dir = proc.working_dir.empty() ? + find_working_directory(proc.cmd, _env) : + boost::filesystem::path(proc.working_dir); + BOOST_LOG(info) << "Executing: ["sv << proc.cmd << "] in ["sv << working_dir << ']'; + _process = platf::run_unprivileged(proc.cmd, working_dir, _env, _pipe.get(), ec, &_process_handle); if(ec) { - BOOST_LOG(warning) << "Couldn't run ["sv << _app.cmd << "]: System: "sv << ec.message(); + BOOST_LOG(warning) << "Couldn't run ["sv << proc.cmd << "]: System: "sv << ec.message(); return -1; } } @@ -202,26 +203,21 @@ void proc_t::terminate() { _process_handle = bp::group(); _app_id = -1; - for(; _app_prep_it != _app_prep_begin; --_app_prep_it) { - auto &cmd = (_app_prep_it - 1)->undo_cmd; + for(; _undo_it != _undo_begin; --_undo_it) { + auto &cmd = (_undo_it - 1)->undo_cmd; if(cmd.empty()) { continue; } - boost::filesystem::path working_dir = _app.working_dir.empty() ? - find_working_directory(cmd, _env) : - boost::filesystem::path(_app.working_dir); - BOOST_LOG(info) << "Executing Undo Cmd: ["sv << cmd << ']'; - auto child = platf::run_unprivileged(cmd, working_dir, _env, _pipe.get(), ec, nullptr); + BOOST_LOG(info) << "Executing: ["sv << cmd << ']'; + + auto ret = exe_with_full_privs(cmd, _env, _pipe, ec); if(ec) { BOOST_LOG(warning) << "System: "sv << ec.message(); } - child.wait(); - auto ret = child.exit_code(); - if(ret != 0) { BOOST_LOG(warning) << "Return code ["sv << ret << ']'; } @@ -453,8 +449,6 @@ std::optional parse(const std::string &file_name) { this_env[name] = parse_env_val(this_env, val.get_value()); } - - std::set ids; std::vector apps; int i = 0; diff --git a/src/process.h b/src/process.h index 99af563d39f..e4bd0170e37 100644 --- a/src/process.h +++ b/src/process.h @@ -87,7 +87,6 @@ class proc_t { boost::process::environment _env; std::vector _apps; - ctx_t _app; // If no command associated with _app_id, yet it's still running bool placebo {}; @@ -96,8 +95,8 @@ class proc_t { boost::process::group _process_handle; file_t _pipe; - std::vector::const_iterator _app_prep_it; - std::vector::const_iterator _app_prep_begin; + std::vector::const_iterator _undo_it; + std::vector::const_iterator _undo_begin; }; /** From a6b2b1c53dbfde21fd5a2f520f9090673126094b Mon Sep 17 00:00:00 2001 From: pgrunzjr Date: Thu, 9 Mar 2023 22:57:47 -0600 Subject: [PATCH 06/14] Move prep commands to config instead of apps --- src/config.cpp | 29 +- src/config.h | 9 + src/confighttp.cpp | 40 -- src/process.cpp | 21 +- src/process.h | 11 +- src_assets/common/assets/web/apps.html | 474 +++++++++-------------- src_assets/common/assets/web/config.html | 65 +++- 7 files changed, 296 insertions(+), 353 deletions(-) diff --git a/src/config.cpp b/src/config.cpp index 89c7dba1917..9b79c7fb3bc 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -7,6 +7,7 @@ #include #include +#include #include "config.h" #include "main.h" @@ -425,7 +426,8 @@ sunshine_t sunshine { platf::appdata().string() + "/sunshine.conf", // config file {}, // cmd args 47989, - platf::appdata().string() + "/sunshine.log", // log file + platf::appdata().string() + "/sunshine.log", // log file, + {} }; bool endline(char ch) { @@ -759,6 +761,30 @@ void list_string_f(std::unordered_map &vars, const std input.emplace_back(begin, pos); } } +void list_prep_cmd_f(std::unordered_map &vars, const std::string &name, std::vector &input) { + std::string string; + string_f(vars, name, string); + + auto json = boost::json::parse(string); + + if (json.if_array()) { + auto list = json.as_array(); + + for(auto &el : list) { + if (el.is_object()) { + auto cmd = el.as_object(); + + input.emplace_back( + cmd.if_contains("do") ? cmd.at("do").as_string().c_str() : "", + cmd.if_contains("undo") ? cmd.at("undo").as_string().c_str() : "" + ); + } + else { + continue; + } + } + } +} void list_int_f(std::unordered_map &vars, const std::string &name, std::vector &input) { std::vector list; @@ -902,6 +928,7 @@ void apply_config(std::unordered_map &&vars) { string_f(vars, "external_ip", nvhttp.external_ip); list_string_f(vars, "resolutions"s, nvhttp.resolutions); list_int_f(vars, "fps"s, nvhttp.fps); + list_prep_cmd_f(vars, "global_prep_cmd", config::sunshine.prep_cmds); string_f(vars, "audio_sink", audio.sink); string_f(vars, "virtual_sink", audio.virtual_sink); diff --git a/src/config.h b/src/config.h index 3a2ff822028..ff7d03268b9 100644 --- a/src/config.h +++ b/src/config.h @@ -118,6 +118,13 @@ enum flag_e : std::size_t { }; } +struct prep_cmd_t { + prep_cmd_t(std::string &&do_cmd, std::string &&undo_cmd) : do_cmd(std::move(do_cmd)), undo_cmd(std::move(undo_cmd)) {} + explicit prep_cmd_t(std::string &&do_cmd) : do_cmd(std::move(do_cmd)) {} + std::string do_cmd; + std::string undo_cmd; +}; + struct sunshine_t { int min_log_level; std::bitset flags; @@ -137,6 +144,8 @@ struct sunshine_t { std::uint16_t port; std::string log_file; + + std::vector prep_cmds; }; extern video_t video; diff --git a/src/confighttp.cpp b/src/confighttp.cpp index b47abba7c1e..5f54aebb8fb 100644 --- a/src/confighttp.cpp +++ b/src/confighttp.cpp @@ -489,45 +489,6 @@ void uploadCover(resp_https_t response, req_https_t request) { outputTree.put("path", path); } -void savePrepCmd(resp_https_t response, req_https_t request) { - if(!authenticate(response, request)) return; - - print_req(request); - - std::stringstream ss; - ss << request->content.rdbuf(); - - pt::ptree outputTree; - auto g = util::fail_guard([&]() { - std::ostringstream data; - - pt::write_json(data, outputTree); - response->write(data.str()); - }); - - pt::ptree inputTree, fileTree; - - BOOST_LOG(fatal) << config::stream.file_apps; - try { - pt::read_json(ss, inputTree); - pt::read_json(config::stream.file_apps, fileTree); - fileTree.erase("global-prep-cmd"); - fileTree.push_back(std::make_pair("global-prep-cmd", inputTree)); - pt::write_json(config::stream.file_apps, fileTree); - } - catch(std::exception &e) { - BOOST_LOG(warning) << "saveGlobalPrepCmd: "sv << e.what(); - - outputTree.put("status", "false"); - outputTree.put("error", "Invalid Input JSON"); - return; - } - - outputTree.put("status", "true"); - proc::refresh(config::stream.file_apps); -} - - void getConfig(resp_https_t response, req_https_t request) { if(!authenticate(response, request)) return; @@ -758,7 +719,6 @@ void start() { server.resource["^/api/apps$"]["GET"] = getApps; server.resource["^/api/logs$"]["GET"] = getLogs; server.resource["^/api/apps$"]["POST"] = saveApp; - server.resource["^/api/apps/prep$"]["POST"] = savePrepCmd; server.resource["^/api/config$"]["GET"] = getConfig; server.resource["^/api/config$"]["POST"] = saveConfig; server.resource["^/api/restart$"]["POST"] = restart; diff --git a/src/process.cpp b/src/process.cpp index 63f3ec28a6d..9636196a78c 100644 --- a/src/process.cpp +++ b/src/process.cpp @@ -22,6 +22,7 @@ #include "main.h" #include "platform/common.h" #include "utility.h" +#include "config.h" #ifdef _WIN32 // _SH constants for _wfsopen() @@ -441,7 +442,6 @@ std::optional parse(const std::string &file_name) { auto &apps_node = tree.get_child("apps"s); auto &env_vars = tree.get_child("env"s); - auto global_prep_nodes_opt = tree.get_child_optional("global-prep-cmd"s); auto this_env = boost::this_process::environment(); @@ -465,20 +465,13 @@ std::optional parse(const std::string &file_name) { auto working_dir = app_node.get_optional("working-dir"s); std::vector prep_cmds; - if(global_prep_nodes_opt && !exclude_global_prep.value_or(false)) { - auto &global_prep_nodes = *global_prep_nodes_opt; + if (!exclude_global_prep.value_or(false)) { + prep_cmds.reserve(config::sunshine.prep_cmds.size()); + for(auto &prep_cmd : config::sunshine.prep_cmds) { + auto do_cmd = parse_env_val(this_env, prep_cmd.do_cmd); + auto undo_cmd = parse_env_val(this_env, prep_cmd.undo_cmd); - prep_cmds.reserve(global_prep_nodes.size()); - for(auto &[_, prep_node] : global_prep_nodes) { - auto do_cmd = parse_env_val(this_env, prep_node.get("do"s)); - auto undo_cmd = prep_node.get_optional("undo"s); - - if(undo_cmd) { - prep_cmds.emplace_back(std::move(do_cmd), parse_env_val(this_env, *undo_cmd)); - } - else { - prep_cmds.emplace_back(std::move(do_cmd)); - } + prep_cmds.emplace_back(std::move(do_cmd), std::move(undo_cmd)); } } diff --git a/src/process.h b/src/process.h index e4bd0170e37..7a91e08f0d9 100644 --- a/src/process.h +++ b/src/process.h @@ -13,19 +13,12 @@ #include #include "utility.h" +#include "config.h" namespace proc { using file_t = util::safe_ptr_v2; -struct cmd_t { - cmd_t(std::string &&do_cmd, std::string &&undo_cmd) : do_cmd(std::move(do_cmd)), undo_cmd(std::move(undo_cmd)) {} - explicit cmd_t(std::string &&do_cmd) : do_cmd(std::move(do_cmd)) {} - - std::string do_cmd; - - // Executed when proc_t has finished running, meant to reverse 'do_cmd' if applicable - std::string undo_cmd; -}; +typedef config::prep_cmd_t cmd_t; /* * pre_cmds -- guaranteed to be executed unless any of the commands fail. * detached -- commands detached from Sunshine diff --git a/src_assets/common/assets/web/apps.html b/src_assets/common/assets/web/apps.html index 1fc8e333958..f026cba01d2 100644 --- a/src_assets/common/assets/web/apps.html +++ b/src_assets/common/assets/web/apps.html @@ -3,271 +3,82 @@

Applications

Applications are refreshed only when Client is restarted
-
- - - -
-
- - - - - - - - - - - - - -
NameActions
{{app.name}} - - -
-
-
-
- -
- - -
- Application Name, as shown on Moonlight -
-
- -
- - -
- The file where the output of the command is stored, if it is not - specified, the output is ignored -
-
- -
-
- - -
- Enable/Disable the execution of Global Prep Commands for this application. -
-
- -
- A list of commands to be run before/after this application.
- If any of the prep-commands fail, starting the application is aborted -
- - - - - - - - - - - - - -
DoUndo
- - - - - -
- -
- -
- -
-
{{c}}
- -
-
- - -
-
- A list of commands to be run and forgotten about -
-
- -
- - -
- The main application, if it is not specified, a processs is started - that sleeps indefinitely -
-
- -
- - -
- The working directory that should be passed to the process. - For example, some applications use the working directory to search for configuration files. - If not set, Sunshine will default to the parent directory of the command -
-
- -
- -
- - - -
-
- Application icon/picture/image path that will be sent to client. Image must be a PNG file. - If not set, Sunshine will send default box image. -
-
- -
- + +
+ + +
+ The file where the output of the command is stored, if it is not + specified, the output is ignored +
-
- -
+
- +
+ + +
+ Enable/Disable the execution of Global Prep Commands for this application. +
+
+
- A list of commands to be run before/after all applications.
- If any of the prep-commands fail, starting the application is aborted. + A list of commands to be run before/after this application.
+ If any of the prep-commands fail, starting the application is aborted
- +
- +
Do Undo
Covers Found @@ -296,14 +107,135 @@ - + +
+ +
+
{{c}}
+ +
+
+ + +
+
+ A list of commands to be run and forgotten about +
+
+ +
+ + +
+ The main application, if it is not specified, a processs is started + that sleeps indefinitely +
+
+ +
+ + +
+ The working directory that should be passed to the process. + For example, some applications use the working directory to search for configuration files. + If not set, Sunshine will default to the parent directory of the command +
+
+ +
+ +
+ + + +
+
+ Application icon/picture/image path that will be sent to client. Image must be a PNG file. + If not set, Sunshine will send default box image. +
+
+ +
+ + +
+
+ +
diff --git a/src_assets/common/assets/web/config.html b/src_assets/common/assets/web/config.html index f4ea4171f25..9cbe39d3012 100644 --- a/src_assets/common/assets/web/config.html +++ b/src_assets/common/assets/web/config.html @@ -213,10 +213,58 @@

Configuration

+
+ It may be possible that you cannot send the Windows Key from Moonlight directly.
+ In those cases it may be useful to make Sunshine think the Right Alt key is the Windows key +
-
- It may be possible that you cannot send the Windows Key from Moonlight directly.
- In those cases it may be useful to make Sunshine think the Right Alt key is the Windows key + +
+ +
+ A list of commands to be run before/after all applications.
+ If any of the prep-commands fail, starting the application is aborted. +
+ + + + + + + + + + + + + +
DoUndo
+ + + + + +
+
@@ -967,6 +1015,7 @@

Configuration

currentTab: "general", resIn: "", fpsIn: "", + global_prep_cmd: [], tabs: [ { id: "general", @@ -1061,6 +1110,9 @@

Configuration

let resolutions = []; res.split(",").forEach((r) => resolutions.push(r.trim())); this.resolutions = resolutions; + + this.config.global_prep_cmd = this.config.global_prep_cmd || []; + this.global_prep_cmd = JSON.parse(this.config.global_prep_cmd); }); }, methods: { @@ -1075,6 +1127,7 @@

Configuration

"]"; // remove quotes from values in fps this.config.fps = JSON.stringify(this.fps).replace(/"/g, ""); + this.config.global_prep_cmd = JSON.stringify(this.global_prep_cmd); }, save() { this.saved = false; @@ -1135,6 +1188,12 @@

Configuration

} }); }, + add_global_prep_cmd() { + this.global_prep_cmd.push({ + do: "", + undo: "", + }); + }, }, }); From fd88fe714b874de0351692b1fd4a2a02f57304de Mon Sep 17 00:00:00 2001 From: pgrunzjr Date: Sat, 11 Mar 2023 16:05:23 -0600 Subject: [PATCH 07/14] Fix comments --- src/config.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/config.cpp b/src/config.cpp index 9b79c7fb3bc..cee75a998c3 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -426,8 +426,8 @@ sunshine_t sunshine { platf::appdata().string() + "/sunshine.conf", // config file {}, // cmd args 47989, - platf::appdata().string() + "/sunshine.log", // log file, - {} + platf::appdata().string() + "/sunshine.log", // log file + {}, // prep commands }; bool endline(char ch) { From aada5016c87d6d8289fa5d68a09040155dba6eac Mon Sep 17 00:00:00 2001 From: pgrunzjr Date: Sat, 11 Mar 2023 16:08:01 -0600 Subject: [PATCH 08/14] Remove unnecessary whitespace --- src/process.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/process.cpp b/src/process.cpp index 9636196a78c..a6a99db2aa3 100644 --- a/src/process.cpp +++ b/src/process.cpp @@ -218,7 +218,7 @@ void proc_t::terminate() { if(ec) { BOOST_LOG(warning) << "System: "sv << ec.message(); } - + if(ret != 0) { BOOST_LOG(warning) << "Return code ["sv << ret << ']'; } @@ -440,8 +440,8 @@ std::optional parse(const std::string &file_name) { try { pt::read_json(file_name, tree); - auto &apps_node = tree.get_child("apps"s); - auto &env_vars = tree.get_child("env"s); + auto &apps_node = tree.get_child("apps"s); + auto &env_vars = tree.get_child("env"s); auto this_env = boost::this_process::environment(); From 0b794d0e52a760d0a0321da7c694f15e10ebe3f6 Mon Sep 17 00:00:00 2001 From: pgrunzjr Date: Sun, 12 Mar 2023 14:28:40 -0500 Subject: [PATCH 09/14] Used boost::property_tree instead of boost::json --- src/config.cpp | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/config.cpp b/src/config.cpp index cee75a998c3..1dd88b8ec91 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include "config.h" #include "main.h" @@ -765,24 +767,22 @@ void list_prep_cmd_f(std::unordered_map &vars, const s std::string string; string_f(vars, name, string); - auto json = boost::json::parse(string); + std::stringstream jsonStream; - if (json.if_array()) { - auto list = json.as_array(); - - for(auto &el : list) { - if (el.is_object()) { - auto cmd = el.as_object(); - - input.emplace_back( - cmd.if_contains("do") ? cmd.at("do").as_string().c_str() : "", - cmd.if_contains("undo") ? cmd.at("undo").as_string().c_str() : "" - ); - } - else { - continue; - } - } + // We need to add a wrapping object to make it valid JSON, otherwise ptree cannot parse it. + jsonStream << "{\"prep_cmd\":" << string << "}"; + + boost::property_tree::ptree jsonTree; + boost::property_tree::read_json(jsonStream, jsonTree); + + for(auto &[_, prep_cmd] : jsonTree.get_child("prep_cmd"s)) { + auto do_cmd = prep_cmd.get("do"s); + auto undo_cmd = prep_cmd.get("undo"s); + + input.emplace_back( + std::move(do_cmd), + std::move(undo_cmd) + ); } } From 9fb02d08bdfbedf6b75fe25699726edefb54754c Mon Sep 17 00:00:00 2001 From: pgrunzjr Date: Sun, 12 Mar 2023 14:49:12 -0500 Subject: [PATCH 10/14] Add advanced_usage documentation --- docs/source/about/advanced_usage.rst | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/source/about/advanced_usage.rst b/docs/source/about/advanced_usage.rst index 3f4cd2ee9b2..d22555f665f 100644 --- a/docs/source/about/advanced_usage.rst +++ b/docs/source/about/advanced_usage.rst @@ -107,6 +107,20 @@ log_path log_path = sunshine.log +global_prep_cmd +^^^^^^^^^^^^^^^ + +**Description** + A list of commands to be run before/after all applications. If any of the prep-commands fail, starting the application is aborted. + +**Default** + ``[]`` + +**Example** + .. code-block:: text + + global_prep_cmd = [{"do":"nircmd.exe setdisplay 1280 720 32 144","undo":"nircmd.exe setdisplay 2560 1440 32 144"}] + Controls -------- From cb2ca6ccc80c3483bb798102d88c4077b1e035a6 Mon Sep 17 00:00:00 2001 From: pgrunzjr Date: Sun, 19 Mar 2023 09:25:44 -0500 Subject: [PATCH 11/14] Add prep command default values --- src_assets/common/assets/web/config.html | 1 + 1 file changed, 1 insertion(+) diff --git a/src_assets/common/assets/web/config.html b/src_assets/common/assets/web/config.html index 9cbe39d3012..9e664ee7ee7 100644 --- a/src_assets/common/assets/web/config.html +++ b/src_assets/common/assets/web/config.html @@ -999,6 +999,7 @@

Configuration

"vt_coder": "auto", "vt_realtime": "enabled", "vt_software": "auto", + "global_prep_cmd": "[]", } new Vue({ From ece1eb63adacc0bd625e292cd8aa57791b660a85 Mon Sep 17 00:00:00 2001 From: pgrunzjr Date: Sun, 26 Mar 2023 21:54:05 -0500 Subject: [PATCH 12/14] Fix linting errors --- src/config.cpp | 11 +++++------ src/config.h | 2 +- src/process.cpp | 4 ++-- src/process.h | 2 +- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/config.cpp b/src/config.cpp index 1dd88b8ec91..54b3ae74edc 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -428,8 +428,8 @@ sunshine_t sunshine { platf::appdata().string() + "/sunshine.conf", // config file {}, // cmd args 47989, - platf::appdata().string() + "/sunshine.log", // log file - {}, // prep commands + platf::appdata().string() + "/sunshine.log", // log file + {}, // prep commands }; bool endline(char ch) { @@ -768,7 +768,7 @@ void list_prep_cmd_f(std::unordered_map &vars, const s string_f(vars, name, string); std::stringstream jsonStream; - + // We need to add a wrapping object to make it valid JSON, otherwise ptree cannot parse it. jsonStream << "{\"prep_cmd\":" << string << "}"; @@ -776,13 +776,12 @@ void list_prep_cmd_f(std::unordered_map &vars, const s boost::property_tree::read_json(jsonStream, jsonTree); for(auto &[_, prep_cmd] : jsonTree.get_child("prep_cmd"s)) { - auto do_cmd = prep_cmd.get("do"s); + auto do_cmd = prep_cmd.get("do"s); auto undo_cmd = prep_cmd.get("undo"s); input.emplace_back( std::move(do_cmd), - std::move(undo_cmd) - ); + std::move(undo_cmd)); } } diff --git a/src/config.h b/src/config.h index ff7d03268b9..28871833a45 100644 --- a/src/config.h +++ b/src/config.h @@ -144,7 +144,7 @@ struct sunshine_t { std::uint16_t port; std::string log_file; - + std::vector prep_cmds; }; diff --git a/src/process.cpp b/src/process.cpp index a6a99db2aa3..16dbe856e75 100644 --- a/src/process.cpp +++ b/src/process.cpp @@ -18,11 +18,11 @@ #include #include +#include "config.h" #include "crypto.h" #include "main.h" #include "platform/common.h" #include "utility.h" -#include "config.h" #ifdef _WIN32 // _SH constants for _wfsopen() @@ -465,7 +465,7 @@ std::optional parse(const std::string &file_name) { auto working_dir = app_node.get_optional("working-dir"s); std::vector prep_cmds; - if (!exclude_global_prep.value_or(false)) { + if(!exclude_global_prep.value_or(false)) { prep_cmds.reserve(config::sunshine.prep_cmds.size()); for(auto &prep_cmd : config::sunshine.prep_cmds) { auto do_cmd = parse_env_val(this_env, prep_cmd.do_cmd); diff --git a/src/process.h b/src/process.h index 7a91e08f0d9..557c890b0c0 100644 --- a/src/process.h +++ b/src/process.h @@ -12,8 +12,8 @@ #include -#include "utility.h" #include "config.h" +#include "utility.h" namespace proc { using file_t = util::safe_ptr_v2; From 249592a62e0d906bad5ab81e6ef7d5014ae25793 Mon Sep 17 00:00:00 2001 From: pgrunzjr Date: Sun, 26 Mar 2023 22:02:20 -0500 Subject: [PATCH 13/14] Missed whitespace --- src/config.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.cpp b/src/config.cpp index 54b3ae74edc..7082f32dc22 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -778,7 +778,7 @@ void list_prep_cmd_f(std::unordered_map &vars, const s for(auto &[_, prep_cmd] : jsonTree.get_child("prep_cmd"s)) { auto do_cmd = prep_cmd.get("do"s); auto undo_cmd = prep_cmd.get("undo"s); - + input.emplace_back( std::move(do_cmd), std::move(undo_cmd)); From 34885da6c4c6dde65a7c8e46e0b7db860fe4d4bb Mon Sep 17 00:00:00 2001 From: pgrunzjr Date: Mon, 27 Mar 2023 09:47:44 -0500 Subject: [PATCH 14/14] remove boost json include --- src/config.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/config.cpp b/src/config.cpp index 7082f32dc22..4a454b41edf 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -7,7 +7,6 @@ #include #include -#include #include #include