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

View File

@@ -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?" :

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>();
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.<xmlattr>.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.<xmlattr>.status_code", 400);
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"));
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.<xmlattr>.status_code", 404);
tree.put("root.<xmlattr>.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.<xmlattr>.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();

View File

@@ -70,7 +70,7 @@ namespace nvhttp {
extract_command_entries(const nlohmann::json& j, const std::string& key);
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.

View File

@@ -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<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) {
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() ?

View File

@@ -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<FILE, int, fclose>;
@@ -108,7 +113,7 @@ namespace proc {
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`