Implement pause/resume commands w/ APOLLO_APP_STATUS envvar
This commit is contained in:
@@ -594,6 +594,7 @@ namespace config {
|
||||
false, // notify_pre_releases
|
||||
false, // legacy_ordering
|
||||
{}, // prep commands
|
||||
{}, // state commands
|
||||
{}, // server commands
|
||||
};
|
||||
|
||||
@@ -1211,6 +1212,7 @@ namespace config {
|
||||
|
||||
string_f(vars, "external_ip", nvhttp.external_ip);
|
||||
list_prep_cmd_f(vars, "global_prep_cmd", config::sunshine.prep_cmds);
|
||||
list_prep_cmd_f(vars, "global_state_cmd", config::sunshine.state_cmds);
|
||||
list_server_cmd_f(vars, "server_cmd", config::sunshine.server_cmds);
|
||||
|
||||
string_f(vars, "audio_sink", audio.sink);
|
||||
|
||||
@@ -282,6 +282,7 @@ namespace config {
|
||||
bool notify_pre_releases;
|
||||
bool legacy_ordering;
|
||||
std::vector<prep_cmd_t> prep_cmds;
|
||||
std::vector<prep_cmd_t> state_cmds;
|
||||
std::vector<server_cmd_t> server_cmds;
|
||||
};
|
||||
|
||||
|
||||
@@ -1285,6 +1285,8 @@ namespace confighttp {
|
||||
|
||||
print_req(request);
|
||||
|
||||
proc::proc.terminate();
|
||||
|
||||
// We may not return from this call
|
||||
platf::restart();
|
||||
}
|
||||
@@ -1304,6 +1306,9 @@ namespace confighttp {
|
||||
print_req(request);
|
||||
|
||||
BOOST_LOG(warning) << "Requested quit from config page!"sv;
|
||||
|
||||
proc::proc.terminate();
|
||||
|
||||
#ifdef _WIN32
|
||||
if (GetConsoleWindow() == NULL) {
|
||||
lifetime::exit_sunshine(ERROR_SHUTDOWN_IN_PROGRESS, true);
|
||||
|
||||
@@ -268,6 +268,9 @@ int main(int argc, char *argv[]) {
|
||||
logging::log_flush();
|
||||
lifetime::debug_trap();
|
||||
};
|
||||
|
||||
proc::proc.terminate();
|
||||
|
||||
force_shutdown = task_pool.pushDelayed(task, 10s).task_id;
|
||||
|
||||
shutdown_event->raise(true);
|
||||
|
||||
142
src/process.cpp
142
src/process.cpp
@@ -127,7 +127,7 @@ namespace proc {
|
||||
}
|
||||
}
|
||||
|
||||
boost::filesystem::path find_working_directory(const std::string &cmd, boost::process::v1::environment &env) {
|
||||
boost::filesystem::path find_working_directory(const std::string &cmd, const boost::process::v1::environment &env) {
|
||||
// Parse the raw command string into parts to get the actual command portion
|
||||
#ifdef _WIN32
|
||||
auto parts = boost::program_options::split_winmain(cmd);
|
||||
@@ -375,6 +375,7 @@ namespace proc {
|
||||
_env["APOLLO_APP_ID"] = _app.id;
|
||||
_env["APOLLO_APP_NAME"] = _app.name;
|
||||
_env["APOLLO_APP_UUID"] = _app.uuid;
|
||||
_env["APOLLO_APP_STATUS"] = "STARTING";
|
||||
_env["APOLLO_CLIENT_UUID"] = launch_session->unique_id;
|
||||
_env["APOLLO_CLIENT_NAME"] = launch_session->device_name;
|
||||
_env["APOLLO_CLIENT_WIDTH"] = std::to_string(render_width);
|
||||
@@ -457,6 +458,8 @@ namespace proc {
|
||||
}
|
||||
}
|
||||
|
||||
_env["APOLLO_APP_STATUS"] = "RUNNING";
|
||||
|
||||
for (auto &cmd : _app.detached) {
|
||||
boost::filesystem::path working_dir = _app.working_dir.empty() ?
|
||||
find_working_directory(cmd, _env) :
|
||||
@@ -593,16 +596,108 @@ namespace proc {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void proc_t::pause() {
|
||||
if (_app.terminate_on_pause) {
|
||||
terminate();
|
||||
} else {
|
||||
#if defined SUNSHINE_TRAY && SUNSHINE_TRAY >= 1
|
||||
system_tray::update_tray_pausing(proc::proc.get_last_run_app_name());
|
||||
#endif
|
||||
void proc_t::resume() {
|
||||
BOOST_LOG(info) << "Session resuming for app [" << _app_name << "].";
|
||||
|
||||
if (!_app.state_cmds.empty()) {
|
||||
auto exec_thread = std::thread([cmd_list = _app.state_cmds, app_working_dir = _app.working_dir, _env = _env]() mutable {
|
||||
|
||||
_env["APOLLO_APP_STATUS"] = "RESUMING";
|
||||
|
||||
std::error_code ec;
|
||||
auto _state_resume_it = std::begin(cmd_list);
|
||||
|
||||
for (; _state_resume_it != std::end(cmd_list); ++_state_resume_it) {
|
||||
auto &cmd = *_state_resume_it;
|
||||
|
||||
// Skip empty commands
|
||||
if (cmd.do_cmd.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
boost::filesystem::path working_dir = app_working_dir.empty() ?
|
||||
find_working_directory(cmd.do_cmd, _env) :
|
||||
boost::filesystem::path(app_working_dir);
|
||||
BOOST_LOG(info) << "Executing Resume Cmd: ["sv << cmd.do_cmd << "] elevated: " << cmd.elevated;
|
||||
auto child = platf::run_command(cmd.elevated, true, cmd.do_cmd, working_dir, _env, nullptr, ec, nullptr);
|
||||
|
||||
if (ec) {
|
||||
BOOST_LOG(error) << "Couldn't run ["sv << cmd.do_cmd << "]: System: "sv << ec.message();
|
||||
break;
|
||||
}
|
||||
|
||||
child.wait();
|
||||
|
||||
auto ret = child.exit_code();
|
||||
if (ret != 0 && ec != std::errc::permission_denied) {
|
||||
BOOST_LOG(error) << '[' << cmd.do_cmd << "] failed with code ["sv << ret << ']';
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
exec_thread.detach();
|
||||
}
|
||||
}
|
||||
|
||||
void proc_t::pause() {
|
||||
if (!running()) {
|
||||
BOOST_LOG(info) << "Session already stopped, do not run pause commands.";
|
||||
return;
|
||||
}
|
||||
|
||||
if (_app.terminate_on_pause) {
|
||||
BOOST_LOG(info) << "Terminating app [" << _app_name << "] when all clients are disconnected. Pause commands are skipped.";
|
||||
terminate();
|
||||
return;
|
||||
}
|
||||
|
||||
BOOST_LOG(info) << "Session pausing for app [" << _app_name << "].";
|
||||
|
||||
if (!_app.state_cmds.empty()) {
|
||||
auto exec_thread = std::thread([cmd_list = _app.state_cmds, app_working_dir = _app.working_dir, _env = _env]() mutable {
|
||||
_env["APOLLO_APP_STATUS"] = "PAUSING";
|
||||
|
||||
std::error_code ec;
|
||||
auto _state_pause_it = std::begin(cmd_list);
|
||||
|
||||
for (; _state_pause_it != std::end(cmd_list); ++_state_pause_it) {
|
||||
auto &cmd = *_state_pause_it;
|
||||
|
||||
// Skip empty commands
|
||||
if (cmd.undo_cmd.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
boost::filesystem::path working_dir = app_working_dir.empty() ?
|
||||
find_working_directory(cmd.undo_cmd, _env) :
|
||||
boost::filesystem::path(app_working_dir);
|
||||
BOOST_LOG(info) << "Executing Pause Cmd: ["sv << cmd.undo_cmd << "] elevated: " << cmd.elevated;
|
||||
auto child = platf::run_command(cmd.elevated, true, cmd.undo_cmd, working_dir, _env, nullptr, ec, nullptr);
|
||||
|
||||
if (ec) {
|
||||
BOOST_LOG(error) << "Couldn't run ["sv << cmd.undo_cmd << "]: System: "sv << ec.message();
|
||||
break;
|
||||
}
|
||||
|
||||
child.wait();
|
||||
|
||||
auto ret = child.exit_code();
|
||||
if (ret != 0 && ec != std::errc::permission_denied) {
|
||||
BOOST_LOG(error) << '[' << cmd.undo_cmd << "] failed with code ["sv << ret << ']';
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
exec_thread.detach();
|
||||
}
|
||||
|
||||
#if defined SUNSHINE_TRAY && SUNSHINE_TRAY >= 1
|
||||
system_tray::update_tray_pausing(proc::proc.get_last_run_app_name());
|
||||
#endif
|
||||
}
|
||||
|
||||
void proc_t::terminate(bool immediate, bool needs_refresh) {
|
||||
std::error_code ec;
|
||||
placebo = false;
|
||||
@@ -614,6 +709,8 @@ namespace proc {
|
||||
_process = boost::process::v1::child();
|
||||
_process_group = boost::process::v1::group();
|
||||
|
||||
_env["APOLLO_APP_STATUS"] = "TERMINATING";
|
||||
|
||||
for (; _app_prep_it != _app_prep_begin; --_app_prep_it) {
|
||||
auto &cmd = *(_app_prep_it - 1);
|
||||
|
||||
@@ -1195,6 +1292,34 @@ namespace proc {
|
||||
}
|
||||
}
|
||||
|
||||
// Build the list of pause/resume commands.
|
||||
std::vector<proc::cmd_t> state_cmds;
|
||||
bool exclude_global_state_cmds = app_node.value("exclude-global-state-cmd", false);
|
||||
if (!exclude_global_state_cmds) {
|
||||
state_cmds.reserve(config::sunshine.state_cmds.size());
|
||||
for (auto &state_cmd : config::sunshine.state_cmds) {
|
||||
auto do_cmd = parse_env_val(this_env, state_cmd.do_cmd);
|
||||
auto undo_cmd = parse_env_val(this_env, state_cmd.undo_cmd);
|
||||
state_cmds.emplace_back(
|
||||
std::move(do_cmd),
|
||||
std::move(undo_cmd),
|
||||
std::move(state_cmd.elevated)
|
||||
);
|
||||
}
|
||||
}
|
||||
if (app_node.contains("state-cmd") && app_node["state-cmd"].is_array()) {
|
||||
for (auto &prep_node : app_node["state-cmd"]) {
|
||||
std::string do_cmd = parse_env_val(this_env, prep_node.value("do", ""));
|
||||
std::string undo_cmd = parse_env_val(this_env, prep_node.value("undo", ""));
|
||||
bool elevated = prep_node.value("elevated", false);
|
||||
state_cmds.emplace_back(
|
||||
std::move(do_cmd),
|
||||
std::move(undo_cmd),
|
||||
std::move(elevated)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Build the list of detached commands.
|
||||
std::vector<std::string> detached;
|
||||
if (app_node.contains("detached") && app_node["detached"].is_array()) {
|
||||
@@ -1243,6 +1368,7 @@ namespace proc {
|
||||
|
||||
ctx.name = std::move(name);
|
||||
ctx.prep_cmds = std::move(prep_cmds);
|
||||
ctx.state_cmds = std::move(state_cmds);
|
||||
ctx.detached = std::move(detached);
|
||||
|
||||
apps.emplace_back(std::move(ctx));
|
||||
|
||||
@@ -62,6 +62,7 @@ namespace proc {
|
||||
*/
|
||||
struct ctx_t {
|
||||
std::vector<cmd_t> prep_cmds;
|
||||
std::vector<cmd_t> state_cmds;
|
||||
|
||||
/**
|
||||
* Some applications, such as Steam, either exit quickly, or keep running indefinitely.
|
||||
@@ -136,6 +137,7 @@ namespace proc {
|
||||
std::string get_last_run_app_name();
|
||||
std::string get_running_app_uuid();
|
||||
boost::process::v1::environment get_env();
|
||||
void resume();
|
||||
void pause();
|
||||
void terminate(bool immediate = false, bool needs_refresh = true);
|
||||
|
||||
@@ -164,7 +166,7 @@ namespace proc {
|
||||
};
|
||||
|
||||
boost::filesystem::path
|
||||
find_working_directory(const std::string &cmd, boost::process::v1::environment &env);
|
||||
find_working_directory(const std::string &cmd, const boost::process::v1::environment &env);
|
||||
|
||||
/**
|
||||
* @brief Calculate a stable id based on name and image data
|
||||
|
||||
@@ -2120,6 +2120,7 @@ namespace stream {
|
||||
// If this is the first session, invoke the platform callbacks
|
||||
if (++running_sessions == 1) {
|
||||
platf::streaming_will_start();
|
||||
proc::proc.resume();
|
||||
}
|
||||
|
||||
if (!session.do_cmds.empty()) {
|
||||
|
||||
@@ -79,12 +79,15 @@ namespace system_tray {
|
||||
void tray_restart_cb(struct tray_menu *item) {
|
||||
BOOST_LOG(info) << "Restarting from system tray"sv;
|
||||
|
||||
proc::proc.terminate();
|
||||
platf::restart();
|
||||
}
|
||||
|
||||
void tray_quit_cb(struct tray_menu *item) {
|
||||
BOOST_LOG(info) << "Quitting from system tray"sv;
|
||||
|
||||
proc::proc.terminate();
|
||||
|
||||
#ifdef _WIN32
|
||||
// If we're running in a service, return a special status to
|
||||
// tell it to terminate too, otherwise it will just respawn us.
|
||||
|
||||
Reference in New Issue
Block a user