Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
72527fa
Fix: Dynamically reload apps.json for Moonlight API if modified on disk
eduardomozart Aug 14, 2026
9f01397
Fix: Dynamically reload apps.json for Moonlight API if modified on disk
eduardomozart Aug 14, 2026
3e8b40d
Merge branch 'fix/applist-reload' of https://github.com/eduardomozart…
eduardomozart Aug 14, 2026
717cfde
Merge branch 'master' into fix/applist-reload
eduardomozart Aug 18, 2026
99ecd7e
fix(process): prevent initial parse skip and preserve running state o…
eduardomozart Sep 29, 2026
908873d
Merge branch 'master' into fix/applist-reload
eduardomozart Sep 29, 2026
5121de6
fix(process): preserve session env vars during refresh and fix tests
eduardomozart Sep 29, 2026
514e186
test(process): silence sonar cpp:S3656 warning for protected members …
eduardomozart Sep 29, 2026
95ed08a
test(process): simplify undo test command using inline strings and by…
eduardomozart Sep 29, 2026
c19554a
test(process): mock undo environment test to bypass ci process launch…
eduardomozart Sep 29, 2026
ed2eae2
test(process): refactor to remove duplicate code blocks and fix sonar…
eduardomozart Sep 29, 2026
19c4e06
test(process): refactor helper to use template parameter and const to…
eduardomozart Sep 29, 2026
57fa788
test(process): refactor to use a dedicated test fixture instead of a …
eduardomozart Sep 29, 2026
85a4384
test(process): add const qualifier to test fixture helper method
eduardomozart Sep 29, 2026
0fbbe26
test(process): apply clang-format and doxygen standards to test fixture
eduardomozart Sep 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/nvhttp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1302,6 +1302,7 @@ namespace nvhttp {
*/
void applist(resp_https_t response, req_https_t request) {
print_req<SunshineHTTPS>(request);
proc::refresh(config::stream.file_apps);
Comment thread
eduardomozart marked this conversation as resolved.

pt::ptree tree;

Expand Down
40 changes: 39 additions & 1 deletion src/process.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,20 @@ namespace proc {
return _app.name;
}

void proc_t::update_apps_and_env(proc_t &&other) {
if (_app_id > 0 || placebo) {
// Preserve session-specific environment variables for the running app
for (const auto &var : _env) {
std::string name = var.get_name();
if (name.find("SUNSHINE_APP_") == 0 || name.find("SUNSHINE_CLIENT_") == 0) {
other._env[name] = var.to_string();
}
}
}
_env = std::move(other._env);
_apps = std::move(other._apps);
}

proc_t::~proc_t() {
// It's not safe to call terminate() here because our proc_t is a static variable
// that may be destroyed after the Boost loggers have been destroyed. Instead,
Expand Down Expand Up @@ -818,12 +832,36 @@ namespace proc {

/**
* @brief Refresh cached platform state from the operating system.
*
* This function compares the current last modified time of the file to the time it was last parsed.
* If the file has been modified since it was last parsed (or if it has never been parsed),
* it will be re-parsed to update the app list.
*
* An optional is used rather than a bare file_time_type because value-initialized
* file_time_type{} is not a reliable "never parsed" sentinel. On some toolchains
* (e.g. MSYS2 UCRT64 GCC 16.2), file_time_type::clock::now() compares
* less-or-equal to file_time_type{}, which would cause the initial parse to be skipped.
*
* When an app is currently running, only the cached app configuration list and environment
* are updated — active process state (_app_id, _process, _process_group, etc.) is preserved
* so that the in-flight streaming session can still be terminated properly.
*/
void refresh(const std::string &file_name) {
static std::optional<std::filesystem::file_time_type> last_apps_file_update; ///< Timestamp of the last successful apps.json parse, or nullopt if never parsed.

std::error_code ec;
auto current_time = std::filesystem::last_write_time(file_name, ec);

// Only skip parsing when we have a known-good timestamp and the file hasn't changed
if (last_apps_file_update && !ec && current_time == *last_apps_file_update) {
return;
}

auto proc_opt = proc::parse(file_name);

if (proc_opt) {
proc = std::move(*proc_opt);
proc.update_apps_and_env(std::move(*proc_opt));
last_apps_file_update = current_time;
}
}
} // namespace proc
20 changes: 20 additions & 0 deletions src/process.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,26 @@ namespace proc {
*/
void terminate();

/**
* @brief Update the cached app list and environment without disturbing running process state.
*
* This replaces only the application configuration data (_apps and _env) from a newly parsed
* proc_t, preserving the current _app_id, _process, _process_group, and other fields that
* track an in-flight streaming session.
*
* @param other The newly parsed proc_t whose apps and env will be moved into this instance.
*/
void update_apps_and_env(proc_t &&other);

#ifdef SUNSHINE_TESTS
/**
* @brief Get the current process environment (strictly for unit tests).
*/
const boost::process::v1::environment &get_env() const {
return _env;
}
#endif

private:
int _app_id;

Expand Down
Loading
Loading