diff --git a/src/confighttp.cpp b/src/confighttp.cpp index ef3ab906..7cfd42c1 100644 --- a/src/confighttp.cpp +++ b/src/confighttp.cpp @@ -1190,15 +1190,14 @@ namespace confighttp { const auto &apps = proc::proc.get_apps(); for (auto &app : apps) { if (app.uuid == uuid) { - auto appid = util::from_view(app.id); crypto::named_cert_t named_cert { .name = "", .uuid = http::unique_id, .perm = crypto::PERM::_all, }; BOOST_LOG(info) << "Launching app ["sv << app.name << "] from web UI"sv; - auto launch_session = nvhttp::make_launch_session(true, false, appid, args, &named_cert); - auto err = proc::proc.execute(appid, app, launch_session); + auto launch_session = nvhttp::make_launch_session(true, false, args, &named_cert); + auto err = proc::proc.execute(app, launch_session); if (err) { bad_request(response, request, err == 503 ? "Failed to initialize video capture/encoding. Is a display connected and turned on?" : diff --git a/src/nvhttp.cpp b/src/nvhttp.cpp index 4ba6b15f..01d90cf0 100644 --- a/src/nvhttp.cpp +++ b/src/nvhttp.cpp @@ -368,7 +368,7 @@ namespace nvhttp { } } - std::shared_ptr make_launch_session(bool host_audio, bool input_only, int appid, const args_t &args, const crypto::named_cert_t* named_cert_p) { + std::shared_ptr make_launch_session(bool host_audio, bool input_only, const args_t &args, const crypto::named_cert_t* named_cert_p) { auto launch_session = std::make_shared(); launch_session->id = ++session_id_counter; @@ -443,7 +443,6 @@ namespace nvhttp { launch_session->device_name = named_cert_p->name.empty() ? "ApolloDisplay"s : named_cert_p->name; launch_session->unique_id = named_cert_p->uuid; launch_session->perm = named_cert_p->perm; - launch_session->appid = appid; launch_session->enable_sops = util::from_view(get_arg(args, "sops", "0")); launch_session->surround_info = util::from_view(get_arg(args, "surroundAudioInfo", "196610")); launch_session->surround_params = (get_arg(args, "surroundParams", "")); @@ -983,9 +982,11 @@ namespace nvhttp { current_appid = 0; } tree.put("root.currentgame", current_appid); + tree.put("root.currentgameuuid", proc::proc.get_running_app_uuid()); tree.put("root.state", current_appid > 0 ? "SUNSHINE_SERVER_BUSY" : "SUNSHINE_SERVER_FREE"); } else { tree.put("root.currentgame", 0); + tree.put("root.currentgameuuid", ""); tree.put("root.state", "SUNSHINE_SERVER_FREE"); } @@ -1126,16 +1127,22 @@ namespace nvhttp { auto args = request->parse_query_string(); - auto appid_str = get_arg(args, "appid"); + auto appid_str = get_arg(args, "appid", "0"); + auto appuuid_str = get_arg(args, "appuuid", ""); auto appid = util::from_view(appid_str); auto current_appid = proc::proc.running(); + auto current_app_uuid = proc::proc.get_running_app_uuid(); bool is_input_only = config::input.enable_input_only_mode && appid == proc::input_only_app_id; auto named_cert_p = get_verified_cert(request); auto perm = PERM::launch; // If we have already launched an app, we should allow clients with view permission to join the input only or current app's session. - if (current_appid > 0 && appid != proc::terminate_app_id && (is_input_only || appid == current_appid)) { + if ( + current_appid > 0 + && (appuuid_str != TERMINATE_APP_UUID || appid != proc::terminate_app_id) + && (is_input_only || appid == current_appid || (!appuuid_str.empty() && appuuid_str == current_app_uuid)) + ) { perm = PERM::_allow_view; } @@ -1152,7 +1159,7 @@ namespace nvhttp { args.find("rikey"s) == std::end(args) || args.find("rikeyid"s) == std::end(args) || args.find("localAudioPlayMode"s) == std::end(args) || - args.find("appid"s) == std::end(args) + (args.find("appid"s) == std::end(args) && args.find("appuuid"s) == std::end(args)) ) { tree.put("root.resume", 0); tree.put("root..status_code", 400); @@ -1163,7 +1170,10 @@ namespace nvhttp { if (!is_input_only) { // Special handling for the "terminate" app - if (config::input.enable_input_only_mode && appid == proc::terminate_app_id) { + if ( + (config::input.enable_input_only_mode && appid == proc::terminate_app_id) + || appuuid_str == TERMINATE_APP_UUID + ) { proc::proc.terminate(); tree.put("root.resume", 0); @@ -1173,7 +1183,14 @@ namespace nvhttp { return; } - if (current_appid > 0 && current_appid != proc::input_only_app_id && appid != current_appid) { + if ( + current_appid > 0 + && current_appid != proc::input_only_app_id + && ( + (appid > 0 && appid != current_appid) + || (!appuuid_str.empty() && appuuid_str != current_app_uuid) + ) + ) { tree.put("root.resume", 0); tree.put("root..status_code", 400); tree.put("root..status_message", "An app is already running on this host"); @@ -1183,7 +1200,7 @@ namespace nvhttp { } host_audio = util::from_view(get_arg(args, "localAudioPlayMode")); - auto launch_session = make_launch_session(host_audio, is_input_only, appid, args, named_cert_p); + auto launch_session = make_launch_session(host_audio, is_input_only, args, named_cert_p); auto encryption_mode = net::encryption_mode_for_address(request->remote_endpoint().address()); if (!launch_session->rtsp_cipher && encryption_mode == config::ENCRYPTION_MODE_MANDATORY) { @@ -1212,8 +1229,8 @@ namespace nvhttp { proc::proc.launch_input_only(); } } - } else if (appid > 0) { - if (appid == current_appid) { + } else if (appid > 0 || !appuuid_str.empty()) { + if (appid == current_appid || (!appuuid_str.empty() && appuuid_str == current_app_uuid)) { // We're basically resuming the same app if (!proc::proc.allow_client_commands) { launch_session->client_do_cmds.clear(); @@ -1236,12 +1253,12 @@ namespace nvhttp { } } else { const auto& apps = proc::proc.get_apps(); - auto app_iter = std::find_if(apps.begin(), apps.end(), [&appid_str](const auto _app) { - return _app.id == appid_str; + auto app_iter = std::find_if(apps.begin(), apps.end(), [&appid_str, &appuuid_str](const auto _app) { + return _app.id == appid_str || _app.uuid == appuuid_str; }); if (app_iter == apps.end()) { - BOOST_LOG(error) << "Couldn't find app with ID ["sv << appid_str << ']'; + BOOST_LOG(error) << "Couldn't find app with ID ["sv << appid_str << "] or UUID ["sv << appuuid_str << ']'; tree.put("root..status_code", 404); tree.put("root..status_message", "Cannot find requested application"); tree.put("root.gamesession", 0); @@ -1253,7 +1270,7 @@ namespace nvhttp { launch_session->client_undo_cmds.clear(); } - auto err = proc::proc.execute(appid, *app_iter, launch_session); + auto err = proc::proc.execute(*app_iter, launch_session); if (err) { tree.put("root..status_code", err); tree.put( @@ -1330,7 +1347,7 @@ namespace nvhttp { if (no_active_sessions && args.find("localAudioPlayMode"s) != std::end(args)) { host_audio = util::from_view(get_arg(args, "localAudioPlayMode")); } - auto launch_session = make_launch_session(host_audio, false, 0, args, named_cert_p); + auto launch_session = make_launch_session(host_audio, false, args, named_cert_p); if (!proc::proc.allow_client_commands) { launch_session->client_do_cmds.clear(); diff --git a/src/nvhttp.h b/src/nvhttp.h index d3574d86..c2f89833 100644 --- a/src/nvhttp.h +++ b/src/nvhttp.h @@ -70,7 +70,7 @@ namespace nvhttp { extract_command_entries(const nlohmann::json& j, const std::string& key); std::shared_ptr - make_launch_session(bool host_audio, bool input_only, int appid, const args_t &args, const crypto::named_cert_t* named_cert_p); + make_launch_session(bool host_audio, bool input_only, const args_t &args, const crypto::named_cert_t* named_cert_p); /** * @brief Setup the nvhttp server. diff --git a/src/process.cpp b/src/process.cpp index a81912fc..a3d853c5 100644 --- a/src/process.cpp +++ b/src/process.cpp @@ -44,10 +44,6 @@ #endif #define DEFAULT_APP_IMAGE_PATH SUNSHINE_ASSETS_DIR "/box.png" -#define VIRTUAL_DISPLAY_UUID "8902CB19-674A-403D-A587-41B092E900BA" -#define FALLBACK_DESKTOP_UUID "EAAC6159-089A-46A9-9E24-6436885F6610" -#define REMOTE_INPUT_UUID "8CB5C136-DA67-4F99-B4A1-F9CD35005CF4" -#define TERMINATE_APP_UUID "E16CBE1B-295D-4632-9A76-EC4180C857D3" namespace proc { using namespace std::literals; @@ -174,7 +170,7 @@ namespace proc { #endif } - int proc_t::execute(int app_id, const ctx_t& app, std::shared_ptr launch_session) { + int proc_t::execute(const ctx_t& app, std::shared_ptr launch_session) { if (_app_id == input_only_app_id) { terminate(); std::this_thread::sleep_for(1s); @@ -184,7 +180,7 @@ namespace proc { } _app = app; - _app_id = app_id; + _app_id = util::from_view(app.id); _app_name = app.name; _launch_session = launch_session; allow_client_commands = app.allow_client_commands; @@ -454,7 +450,7 @@ namespace proc { } if (_app.cmd.empty()) { - BOOST_LOG(info) << "Executing [Desktop]"sv; + BOOST_LOG(info) << "No commands configured, showing desktop..."sv; placebo = true; } else { boost::filesystem::path working_dir = _app.working_dir.empty() ? diff --git a/src/process.h b/src/process.h index ddd452d8..595f7700 100644 --- a/src/process.h +++ b/src/process.h @@ -27,6 +27,11 @@ #include "platform/windows/virtual_display.h" #endif +#define VIRTUAL_DISPLAY_UUID "8902CB19-674A-403D-A587-41B092E900BA" +#define FALLBACK_DESKTOP_UUID "EAAC6159-089A-46A9-9E24-6436885F6610" +#define REMOTE_INPUT_UUID "8CB5C136-DA67-4F99-B4A1-F9CD35005CF4" +#define TERMINATE_APP_UUID "E16CBE1B-295D-4632-9A76-EC4180C857D3" + namespace proc { using file_t = util::safe_ptr_v2; @@ -108,7 +113,7 @@ namespace proc { void launch_input_only(); - int execute(int app_id, const ctx_t& _app, std::shared_ptr launch_session); + int execute(const ctx_t& _app, std::shared_ptr launch_session); /** * @return `_app_id` if a process is running, otherwise returns `0`