Support launching app with UUID

This commit is contained in:
Yukino Song
2025-05-13 18:17:44 +08:00
parent 96968b8bb2
commit 1798887e86
5 changed files with 44 additions and 27 deletions
+2 -3
View File
@@ -1190,15 +1190,14 @@ namespace confighttp {
const auto &apps = proc::proc.get_apps(); const auto &apps = proc::proc.get_apps();
for (auto &app : apps) { for (auto &app : apps) {
if (app.uuid == uuid) { if (app.uuid == uuid) {
auto appid = util::from_view(app.id);
crypto::named_cert_t named_cert { crypto::named_cert_t named_cert {
.name = "", .name = "",
.uuid = http::unique_id, .uuid = http::unique_id,
.perm = crypto::PERM::_all, .perm = crypto::PERM::_all,
}; };
BOOST_LOG(info) << "Launching app ["sv << app.name << "] from web UI"sv; 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 launch_session = nvhttp::make_launch_session(true, false, args, &named_cert);
auto err = proc::proc.execute(appid, app, launch_session); auto err = proc::proc.execute(app, launch_session);
if (err) { if (err) {
bad_request(response, request, err == 503 ? bad_request(response, request, err == 503 ?
"Failed to initialize video capture/encoding. Is a display connected and turned on?" : "Failed to initialize video capture/encoding. Is a display connected and turned on?" :
+32 -15
View File
@@ -368,7 +368,7 @@ namespace nvhttp {
} }
} }
std::shared_ptr<rtsp_stream::launch_session_t> 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<rtsp_stream::launch_session_t> 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<rtsp_stream::launch_session_t>(); auto launch_session = std::make_shared<rtsp_stream::launch_session_t>();
launch_session->id = ++session_id_counter; 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->device_name = named_cert_p->name.empty() ? "ApolloDisplay"s : named_cert_p->name;
launch_session->unique_id = named_cert_p->uuid; launch_session->unique_id = named_cert_p->uuid;
launch_session->perm = named_cert_p->perm; 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->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_info = util::from_view(get_arg(args, "surroundAudioInfo", "196610"));
launch_session->surround_params = (get_arg(args, "surroundParams", "")); launch_session->surround_params = (get_arg(args, "surroundParams", ""));
@@ -983,9 +982,11 @@ namespace nvhttp {
current_appid = 0; current_appid = 0;
} }
tree.put("root.currentgame", current_appid); 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"); tree.put("root.state", current_appid > 0 ? "SUNSHINE_SERVER_BUSY" : "SUNSHINE_SERVER_FREE");
} else { } else {
tree.put("root.currentgame", 0); tree.put("root.currentgame", 0);
tree.put("root.currentgameuuid", "");
tree.put("root.state", "SUNSHINE_SERVER_FREE"); tree.put("root.state", "SUNSHINE_SERVER_FREE");
} }
@@ -1126,16 +1127,22 @@ namespace nvhttp {
auto args = request->parse_query_string(); 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 appid = util::from_view(appid_str);
auto current_appid = proc::proc.running(); 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; 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 named_cert_p = get_verified_cert(request);
auto perm = PERM::launch; 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 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; perm = PERM::_allow_view;
} }
@@ -1152,7 +1159,7 @@ namespace nvhttp {
args.find("rikey"s) == std::end(args) || args.find("rikey"s) == std::end(args) ||
args.find("rikeyid"s) == std::end(args) || args.find("rikeyid"s) == std::end(args) ||
args.find("localAudioPlayMode"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.resume", 0);
tree.put("root.<xmlattr>.status_code", 400); tree.put("root.<xmlattr>.status_code", 400);
@@ -1163,7 +1170,10 @@ namespace nvhttp {
if (!is_input_only) { if (!is_input_only) {
// Special handling for the "terminate" app // 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(); proc::proc.terminate();
tree.put("root.resume", 0); tree.put("root.resume", 0);
@@ -1173,7 +1183,14 @@ namespace nvhttp {
return; 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.resume", 0);
tree.put("root.<xmlattr>.status_code", 400); tree.put("root.<xmlattr>.status_code", 400);
tree.put("root.<xmlattr>.status_message", "An app is already running on this host"); tree.put("root.<xmlattr>.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")); 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()); auto encryption_mode = net::encryption_mode_for_address(request->remote_endpoint().address());
if (!launch_session->rtsp_cipher && encryption_mode == config::ENCRYPTION_MODE_MANDATORY) { if (!launch_session->rtsp_cipher && encryption_mode == config::ENCRYPTION_MODE_MANDATORY) {
@@ -1212,8 +1229,8 @@ namespace nvhttp {
proc::proc.launch_input_only(); proc::proc.launch_input_only();
} }
} }
} else if (appid > 0) { } else if (appid > 0 || !appuuid_str.empty()) {
if (appid == current_appid) { if (appid == current_appid || (!appuuid_str.empty() && appuuid_str == current_app_uuid)) {
// We're basically resuming the same app // We're basically resuming the same app
if (!proc::proc.allow_client_commands) { if (!proc::proc.allow_client_commands) {
launch_session->client_do_cmds.clear(); launch_session->client_do_cmds.clear();
@@ -1236,12 +1253,12 @@ namespace nvhttp {
} }
} else { } else {
const auto& apps = proc::proc.get_apps(); const auto& apps = proc::proc.get_apps();
auto app_iter = std::find_if(apps.begin(), apps.end(), [&appid_str](const auto _app) { auto app_iter = std::find_if(apps.begin(), apps.end(), [&appid_str, &appuuid_str](const auto _app) {
return _app.id == appid_str; return _app.id == appid_str || _app.uuid == appuuid_str;
}); });
if (app_iter == apps.end()) { 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.<xmlattr>.status_code", 404); tree.put("root.<xmlattr>.status_code", 404);
tree.put("root.<xmlattr>.status_message", "Cannot find requested application"); tree.put("root.<xmlattr>.status_message", "Cannot find requested application");
tree.put("root.gamesession", 0); tree.put("root.gamesession", 0);
@@ -1253,7 +1270,7 @@ namespace nvhttp {
launch_session->client_undo_cmds.clear(); 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) { if (err) {
tree.put("root.<xmlattr>.status_code", err); tree.put("root.<xmlattr>.status_code", err);
tree.put( tree.put(
@@ -1330,7 +1347,7 @@ namespace nvhttp {
if (no_active_sessions && args.find("localAudioPlayMode"s) != std::end(args)) { if (no_active_sessions && args.find("localAudioPlayMode"s) != std::end(args)) {
host_audio = util::from_view(get_arg(args, "localAudioPlayMode")); 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) { if (!proc::proc.allow_client_commands) {
launch_session->client_do_cmds.clear(); launch_session->client_do_cmds.clear();
+1 -1
View File
@@ -70,7 +70,7 @@ namespace nvhttp {
extract_command_entries(const nlohmann::json& j, const std::string& key); extract_command_entries(const nlohmann::json& j, const std::string& key);
std::shared_ptr<rtsp_stream::launch_session_t> std::shared_ptr<rtsp_stream::launch_session_t>
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. * @brief Setup the nvhttp server.
+3 -7
View File
@@ -44,10 +44,6 @@
#endif #endif
#define DEFAULT_APP_IMAGE_PATH SUNSHINE_ASSETS_DIR "/box.png" #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 { namespace proc {
using namespace std::literals; using namespace std::literals;
@@ -174,7 +170,7 @@ namespace proc {
#endif #endif
} }
int proc_t::execute(int app_id, const ctx_t& app, std::shared_ptr<rtsp_stream::launch_session_t> launch_session) { int proc_t::execute(const ctx_t& app, std::shared_ptr<rtsp_stream::launch_session_t> launch_session) {
if (_app_id == input_only_app_id) { if (_app_id == input_only_app_id) {
terminate(); terminate();
std::this_thread::sleep_for(1s); std::this_thread::sleep_for(1s);
@@ -184,7 +180,7 @@ namespace proc {
} }
_app = app; _app = app;
_app_id = app_id; _app_id = util::from_view(app.id);
_app_name = app.name; _app_name = app.name;
_launch_session = launch_session; _launch_session = launch_session;
allow_client_commands = app.allow_client_commands; allow_client_commands = app.allow_client_commands;
@@ -454,7 +450,7 @@ namespace proc {
} }
if (_app.cmd.empty()) { if (_app.cmd.empty()) {
BOOST_LOG(info) << "Executing [Desktop]"sv; BOOST_LOG(info) << "No commands configured, showing desktop..."sv;
placebo = true; placebo = true;
} else { } else {
boost::filesystem::path working_dir = _app.working_dir.empty() ? boost::filesystem::path working_dir = _app.working_dir.empty() ?
+6 -1
View File
@@ -27,6 +27,11 @@
#include "platform/windows/virtual_display.h" #include "platform/windows/virtual_display.h"
#endif #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 { namespace proc {
using file_t = util::safe_ptr_v2<FILE, int, fclose>; using file_t = util::safe_ptr_v2<FILE, int, fclose>;
@@ -108,7 +113,7 @@ namespace proc {
void launch_input_only(); void launch_input_only();
int execute(int app_id, const ctx_t& _app, std::shared_ptr<rtsp_stream::launch_session_t> launch_session); int execute(const ctx_t& _app, std::shared_ptr<rtsp_stream::launch_session_t> launch_session);
/** /**
* @return `_app_id` if a process is running, otherwise returns `0` * @return `_app_id` if a process is running, otherwise returns `0`