mirror of
https://github.com/LizardByte/Sunshine.git
synced 2025-02-22 03:40:43 +00:00
Implement restart support for all platforms
This commit is contained in:
parent
4668ff59e5
commit
50f689ff80
@ -544,7 +544,6 @@ namespace confighttp {
|
|||||||
outputTree.put("status", "true");
|
outputTree.put("status", "true");
|
||||||
outputTree.put("platform", SUNSHINE_PLATFORM);
|
outputTree.put("platform", SUNSHINE_PLATFORM);
|
||||||
outputTree.put("version", PROJECT_VER);
|
outputTree.put("version", PROJECT_VER);
|
||||||
outputTree.put("restart_supported", platf::restart_supported());
|
|
||||||
|
|
||||||
auto vars = config::parse_config(read_file(config::sunshine.config_file.c_str()));
|
auto vars = config::parse_config(read_file(config::sunshine.config_file.c_str()));
|
||||||
|
|
||||||
@ -595,30 +594,8 @@ namespace confighttp {
|
|||||||
|
|
||||||
print_req(request);
|
print_req(request);
|
||||||
|
|
||||||
std::stringstream ss;
|
// We may not return from this call
|
||||||
std::stringstream configStream;
|
platf::restart();
|
||||||
ss << request->content.rdbuf();
|
|
||||||
pt::ptree outputTree;
|
|
||||||
auto g = util::fail_guard([&]() {
|
|
||||||
std::ostringstream data;
|
|
||||||
|
|
||||||
pt::write_json(data, outputTree);
|
|
||||||
response->write(data.str());
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!platf::restart_supported()) {
|
|
||||||
outputTree.put("status", false);
|
|
||||||
outputTree.put("error", "Restart is not currently supported on this platform");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!platf::restart()) {
|
|
||||||
outputTree.put("status", false);
|
|
||||||
outputTree.put("error", "Restart failed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
outputTree.put("status", true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
11
src/main.cpp
11
src/main.cpp
@ -108,6 +108,7 @@ namespace version {
|
|||||||
} // namespace version
|
} // namespace version
|
||||||
|
|
||||||
namespace lifetime {
|
namespace lifetime {
|
||||||
|
static char **argv;
|
||||||
static std::atomic_int desired_exit_code;
|
static std::atomic_int desired_exit_code;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -130,6 +131,14 @@ namespace lifetime {
|
|||||||
std::this_thread::sleep_for(1s);
|
std::this_thread::sleep_for(1s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gets the argv array passed to main()
|
||||||
|
*/
|
||||||
|
char **
|
||||||
|
get_argv() {
|
||||||
|
return argv;
|
||||||
|
}
|
||||||
} // namespace lifetime
|
} // namespace lifetime
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -207,6 +216,8 @@ SessionMonitorWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
|
|||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
main(int argc, char *argv[]) {
|
main(int argc, char *argv[]) {
|
||||||
|
lifetime::argv = argv;
|
||||||
|
|
||||||
task_pool_util::TaskPool::task_id_t force_shutdown = nullptr;
|
task_pool_util::TaskPool::task_id_t force_shutdown = nullptr;
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
@ -69,6 +69,8 @@ namespace mail {
|
|||||||
namespace lifetime {
|
namespace lifetime {
|
||||||
void
|
void
|
||||||
exit_sunshine(int exit_code, bool async);
|
exit_sunshine(int exit_code, bool async);
|
||||||
|
char **
|
||||||
|
get_argv();
|
||||||
} // namespace lifetime
|
} // namespace lifetime
|
||||||
|
|
||||||
#endif // SUNSHINE_MAIN_H
|
#endif // SUNSHINE_MAIN_H
|
||||||
|
@ -399,9 +399,7 @@ namespace platf {
|
|||||||
void
|
void
|
||||||
streaming_will_stop();
|
streaming_will_stop();
|
||||||
|
|
||||||
bool
|
void
|
||||||
restart_supported();
|
|
||||||
bool
|
|
||||||
restart();
|
restart();
|
||||||
|
|
||||||
struct batched_send_info_t {
|
struct batched_send_info_t {
|
||||||
|
@ -193,16 +193,34 @@ namespace platf {
|
|||||||
// Nothing to do
|
// Nothing to do
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
void
|
||||||
restart_supported() {
|
restart_on_exit() {
|
||||||
// Restart not supported yet
|
char executable[PATH_MAX];
|
||||||
return false;
|
ssize_t len = readlink("/proc/self/exe", executable, PATH_MAX - 1);
|
||||||
|
if (len == -1) {
|
||||||
|
BOOST_LOG(fatal) << "readlink() failed: "sv << errno;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
executable[len] = '\0';
|
||||||
|
|
||||||
|
// ASIO doesn't use O_CLOEXEC, so we have to close all fds ourselves
|
||||||
|
int openmax = (int) sysconf(_SC_OPEN_MAX);
|
||||||
|
for (int fd = STDERR_FILENO + 1; fd < openmax; fd++) {
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-exec ourselves with the same arguments
|
||||||
|
if (execv(executable, lifetime::get_argv()) < 0) {
|
||||||
|
BOOST_LOG(fatal) << "execv() failed: "sv << errno;
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
void
|
||||||
restart() {
|
restart() {
|
||||||
// Restart not supported yet
|
// Gracefully clean up and restart ourselves instead of exiting
|
||||||
return false;
|
atexit(restart_on_exit);
|
||||||
|
lifetime::exit_sunshine(0, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <ifaddrs.h>
|
#include <ifaddrs.h>
|
||||||
|
#include <mach-o/dyld.h>
|
||||||
#include <net/if_dl.h>
|
#include <net/if_dl.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
|
|
||||||
@ -192,16 +193,33 @@ namespace platf {
|
|||||||
// Nothing to do
|
// Nothing to do
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
void
|
||||||
restart_supported() {
|
restart_on_exit() {
|
||||||
// Restart not supported yet
|
char executable[2048];
|
||||||
return false;
|
uint32_t size = sizeof(executable);
|
||||||
|
if (_NSGetExecutablePath(executable, &size) < 0) {
|
||||||
|
BOOST_LOG(fatal) << "NSGetExecutablePath() failed: "sv << errno;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ASIO doesn't use O_CLOEXEC, so we have to close all fds ourselves
|
||||||
|
int openmax = (int) sysconf(_SC_OPEN_MAX);
|
||||||
|
for (int fd = STDERR_FILENO + 1; fd < openmax; fd++) {
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-exec ourselves with the same arguments
|
||||||
|
if (execv(executable, lifetime::get_argv()) < 0) {
|
||||||
|
BOOST_LOG(fatal) << "execv() failed: "sv << errno;
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
void
|
||||||
restart() {
|
restart() {
|
||||||
// Restart not supported yet
|
// Gracefully clean up and restart ourselves instead of exiting
|
||||||
return false;
|
atexit(restart_on_exit);
|
||||||
|
lifetime::exit_sunshine(0, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
@ -809,19 +809,49 @@ namespace platf {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
void
|
||||||
restart_supported() {
|
restart_on_exit() {
|
||||||
// Restart is supported if we're running from the service
|
STARTUPINFOEXW startup_info {};
|
||||||
return (GetConsoleWindow() == NULL);
|
startup_info.StartupInfo.cb = sizeof(startup_info);
|
||||||
|
|
||||||
|
WCHAR executable[MAX_PATH];
|
||||||
|
if (GetModuleFileNameW(NULL, executable, ARRAYSIZE(executable)) == 0) {
|
||||||
|
auto winerr = GetLastError();
|
||||||
|
BOOST_LOG(fatal) << "Failed to get Sunshine path: "sv << winerr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PROCESS_INFORMATION process_info;
|
||||||
|
if (!CreateProcessW(executable,
|
||||||
|
GetCommandLineW(),
|
||||||
|
nullptr,
|
||||||
|
nullptr,
|
||||||
|
false,
|
||||||
|
CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT,
|
||||||
|
nullptr,
|
||||||
|
nullptr,
|
||||||
|
(LPSTARTUPINFOW) &startup_info,
|
||||||
|
&process_info)) {
|
||||||
|
auto winerr = GetLastError();
|
||||||
|
BOOST_LOG(fatal) << "Unable to restart Sunshine: "sv << winerr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseHandle(process_info.hProcess);
|
||||||
|
CloseHandle(process_info.hThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
void
|
||||||
restart() {
|
restart() {
|
||||||
// Gracefully exit. The service will restart us in a few seconds.
|
// If we're running standalone, we have to respawn ourselves via CreateProcess().
|
||||||
// We use an async exit call here because we can't block the
|
// If we're running from the service, we should just exit and let it respawn us.
|
||||||
// HTTP thread or we'll hang shutdown.
|
if (GetConsoleWindow() != NULL) {
|
||||||
|
// Avoid racing with the new process by waiting until we're exiting to start it.
|
||||||
|
atexit(restart_on_exit);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We use an async exit call here because we can't block the HTTP thread or we'll hang shutdown.
|
||||||
lifetime::exit_sunshine(0, true);
|
lifetime::exit_sunshine(0, true);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SOCKADDR_IN
|
SOCKADDR_IN
|
||||||
|
@ -123,6 +123,17 @@ namespace system_tray {
|
|||||||
open_url("https://www.paypal.com/paypalme/ReenigneArcher");
|
open_url("https://www.paypal.com/paypalme/ReenigneArcher");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Callback for restarting Sunshine from the system tray.
|
||||||
|
* @param item The tray menu item.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
tray_restart_cb(struct tray_menu *item) {
|
||||||
|
BOOST_LOG(info) << "Restarting from system tray"sv;
|
||||||
|
|
||||||
|
platf::restart();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Callback for exiting Sunshine from the system tray.
|
* @brief Callback for exiting Sunshine from the system tray.
|
||||||
* @param item The tray menu item.
|
* @param item The tray menu item.
|
||||||
@ -162,6 +173,7 @@ namespace system_tray {
|
|||||||
{ .text = "PayPal", .cb = tray_donate_paypal_cb },
|
{ .text = "PayPal", .cb = tray_donate_paypal_cb },
|
||||||
{ .text = nullptr } } },
|
{ .text = nullptr } } },
|
||||||
{ .text = "-" },
|
{ .text = "-" },
|
||||||
|
{ .text = "Restart", .cb = tray_restart_cb },
|
||||||
{ .text = "Quit", .cb = tray_quit_cb },
|
{ .text = "Quit", .cb = tray_quit_cb },
|
||||||
{ .text = nullptr } },
|
{ .text = nullptr } },
|
||||||
};
|
};
|
||||||
|
@ -965,18 +965,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="alert alert-success my-4" v-if="saved && restart_supported">
|
<div class="alert alert-success my-4" v-if="saved && !restarted">
|
||||||
<b>Success!</b> Click 'Apply' to restart Sunshine and apply changes. This will terminate any running sessions.
|
<b>Success!</b> Click 'Apply' to restart Sunshine and apply changes. This will terminate any running sessions.
|
||||||
</div>
|
</div>
|
||||||
<div class="alert alert-success my-4" v-if="saved && !restart_supported">
|
|
||||||
<b>Success!</b> Restart Sunshine to apply changes.
|
|
||||||
</div>
|
|
||||||
<div class="alert alert-success my-4" v-if="restarted">
|
<div class="alert alert-success my-4" v-if="restarted">
|
||||||
<b>Success!</b> Sunshine is restarting to apply changes.
|
<b>Success!</b> Sunshine is restarting to apply changes.
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3 buttons">
|
<div class="mb-3 buttons">
|
||||||
<button class="btn btn-primary" @click="save">Save</button>
|
<button class="btn btn-primary" @click="save">Save</button>
|
||||||
<button class="btn btn-success" @click="apply" v-if="saved && restart_supported && !restarted">Apply</button>
|
<button class="btn btn-success" @click="apply" v-if="saved && !restarted">Apply</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -1023,7 +1020,6 @@
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
platform: "",
|
platform: "",
|
||||||
restart_supported: false,
|
|
||||||
saved: false,
|
saved: false,
|
||||||
restarted: false,
|
restarted: false,
|
||||||
config: null,
|
config: null,
|
||||||
@ -1087,7 +1083,6 @@
|
|||||||
.then((r) => {
|
.then((r) => {
|
||||||
this.config = r;
|
this.config = r;
|
||||||
this.platform = this.config.platform;
|
this.platform = this.config.platform;
|
||||||
this.restart_supported = (this.config.restart_supported === "true");
|
|
||||||
|
|
||||||
var app = document.getElementById("app");
|
var app = document.getElementById("app");
|
||||||
if (this.platform === "windows") {
|
if (this.platform === "windows") {
|
||||||
@ -1108,7 +1103,6 @@
|
|||||||
|
|
||||||
// remove values we don't want in the config file
|
// remove values we don't want in the config file
|
||||||
delete this.config.platform;
|
delete this.config.platform;
|
||||||
delete this.config.restart_supported;
|
|
||||||
delete this.config.status;
|
delete this.config.status;
|
||||||
delete this.config.version;
|
delete this.config.version;
|
||||||
//Populate default values if not present in config
|
//Populate default values if not present in config
|
||||||
@ -1197,10 +1191,12 @@
|
|||||||
|
|
||||||
saved.then((result) => {
|
saved.then((result) => {
|
||||||
if (result === true) {
|
if (result === true) {
|
||||||
|
this.restarted = true;
|
||||||
|
setTimeout(() => {
|
||||||
|
this.saved = this.restarted = false;
|
||||||
|
}, 5000);
|
||||||
fetch("/api/restart", {
|
fetch("/api/restart", {
|
||||||
method: "POST"
|
method: "POST"
|
||||||
}).then((r) => {
|
|
||||||
if (r.status === 200) this.restarted = true;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!--Restart Sunshine-->
|
<!--Restart Sunshine-->
|
||||||
<div class="card p-2 my-4" v-if="restartSupported">
|
<div class="card p-2 my-4">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h2>Restart Sunshine</h2>
|
<h2>Restart Sunshine</h2>
|
||||||
<br />
|
<br />
|
||||||
@ -31,12 +31,9 @@
|
|||||||
If Sunshine isn't working properly, you can try restarting it.
|
If Sunshine isn't working properly, you can try restarting it.
|
||||||
This will terminate any running sessions.
|
This will terminate any running sessions.
|
||||||
</p>
|
</p>
|
||||||
<div class="alert alert-success" v-if="restartStatus === true">
|
<div class="alert alert-success" v-if="restartPressed === true">
|
||||||
Sunshine is restarting
|
Sunshine is restarting
|
||||||
</div>
|
</div>
|
||||||
<div class="alert alert-danger" v-if="restartStatus === false">
|
|
||||||
Error restarting Sunshine
|
|
||||||
</div>
|
|
||||||
<div>
|
<div>
|
||||||
<button class="btn btn-warning" :disabled="restartPressed" @click="restart">
|
<button class="btn btn-warning" :disabled="restartPressed" @click="restart">
|
||||||
Restart Sunshine
|
Restart Sunshine
|
||||||
@ -90,9 +87,7 @@
|
|||||||
closeAppStatus: null,
|
closeAppStatus: null,
|
||||||
unpairAllPressed: false,
|
unpairAllPressed: false,
|
||||||
unpairAllStatus: null,
|
unpairAllStatus: null,
|
||||||
restartSupported: false,
|
|
||||||
restartPressed: false,
|
restartPressed: false,
|
||||||
restartStatus: null,
|
|
||||||
logs: 'Loading...',
|
logs: 'Loading...',
|
||||||
logFilter: null,
|
logFilter: null,
|
||||||
logInterval: null,
|
logInterval: null,
|
||||||
@ -111,11 +106,6 @@
|
|||||||
this.refreshLogs();
|
this.refreshLogs();
|
||||||
}, 5000);
|
}, 5000);
|
||||||
this.refreshLogs();
|
this.refreshLogs();
|
||||||
fetch("/api/config")
|
|
||||||
.then((r) => r.json())
|
|
||||||
.then((r) => {
|
|
||||||
this.restartSupported = (r.restart_supported === "true");
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
beforeDestroy(){
|
beforeDestroy(){
|
||||||
clearInterval(this.logInterval);
|
clearInterval(this.logInterval);
|
||||||
@ -157,17 +147,11 @@
|
|||||||
},
|
},
|
||||||
restart() {
|
restart() {
|
||||||
this.restartPressed = true;
|
this.restartPressed = true;
|
||||||
|
setTimeout(() => {
|
||||||
|
this.restartPressed = false;
|
||||||
|
}, 5000);
|
||||||
fetch("/api/restart", {
|
fetch("/api/restart", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
}).then((r) => {
|
|
||||||
this.restartPressed = false;
|
|
||||||
|
|
||||||
// We won't get a response in the success case
|
|
||||||
this.restartStatus = r.status.toString() !== "false";
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
this.restartStatus = null;
|
|
||||||
}, 5000);
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user