-
-
Notifications
You must be signed in to change notification settings - Fork 1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(main): move remaining entry related code
- Loading branch information
1 parent
78ed91a
commit eb78805
Showing
29 changed files
with
481 additions
and
418 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
entry_handler | ||
============= | ||
|
||
.. doxygenfile:: entry_handler.h | ||
:allow-dot-graphs: |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,347 @@ | ||
/** | ||
* @file entry_handler.cpp | ||
* @brief Entry point related functions. | ||
*/ | ||
|
||
// standard includes | ||
#include <csignal> | ||
#include <iostream> | ||
#include <thread> | ||
|
||
// local includes | ||
#include "config.h" | ||
#include "confighttp.h" | ||
#include "entry_handler.h" | ||
#include "httpcommon.h" | ||
#include "logging.h" | ||
#include "network.h" | ||
#include "platform/common.h" | ||
#include "thread_pool.h" | ||
#include "version.h" | ||
|
||
extern "C" { | ||
#ifdef _WIN32 | ||
#include <iphlpapi.h> | ||
#endif | ||
} | ||
|
||
using namespace std::literals; | ||
|
||
/** | ||
* @brief Launch the Web UI. | ||
* | ||
* EXAMPLES: | ||
* ```cpp | ||
* launch_ui(); | ||
* ``` | ||
*/ | ||
void | ||
launch_ui() { | ||
std::string url = "https://localhost:" + std::to_string(net::map_port(confighttp::PORT_HTTPS)); | ||
platf::open_url(url); | ||
} | ||
|
||
/** | ||
* @brief Launch the Web UI at a specific endpoint. | ||
* | ||
* EXAMPLES: | ||
* ```cpp | ||
* launch_ui_with_path("/pin"); | ||
* ``` | ||
*/ | ||
void | ||
launch_ui_with_path(std::string path) { | ||
std::string url = "https://localhost:" + std::to_string(net::map_port(confighttp::PORT_HTTPS)) + path; | ||
platf::open_url(url); | ||
} | ||
|
||
namespace gen_creds { | ||
int | ||
entry(const char *name, int argc, char *argv[]) { | ||
if (argc < 2 || argv[0] == "help"sv || argv[1] == "help"sv) { | ||
print_help(name); | ||
return 0; | ||
} | ||
|
||
http::save_user_creds(config::sunshine.credentials_file, argv[0], argv[1]); | ||
|
||
return 0; | ||
} | ||
} // namespace gen_creds | ||
|
||
namespace help { | ||
int | ||
entry(const char *name, int argc, char *argv[]) { | ||
print_help(name); | ||
return 0; | ||
} | ||
} // namespace help | ||
|
||
namespace version { | ||
int | ||
entry(const char *name, int argc, char *argv[]) { | ||
std::cout << PROJECT_NAME << " version: v" << PROJECT_VER << std::endl; | ||
return 0; | ||
} | ||
} // namespace version | ||
|
||
#ifdef _WIN32 | ||
// Define global singleton used for NVIDIA control panel modifications | ||
nvprefs::nvprefs_interface nvprefs_instance; | ||
|
||
namespace restore_nvprefs_undo { | ||
int | ||
entry(const char *name, int argc, char *argv[]) { | ||
// Restore global NVIDIA control panel settings to the undo file | ||
// left by improper termination of sunshine.exe, if it exists. | ||
// This entry point is typically called by the uninstaller. | ||
if (nvprefs_instance.load()) { | ||
nvprefs_instance.restore_from_and_delete_undo_file_if_exists(); | ||
nvprefs_instance.unload(); | ||
} | ||
return 0; | ||
} | ||
} // namespace restore_nvprefs_undo | ||
#endif | ||
|
||
namespace lifetime { | ||
char **argv; | ||
std::atomic_int desired_exit_code; | ||
|
||
/** | ||
* @brief Terminates Sunshine gracefully with the provided exit code. | ||
* @param exit_code The exit code to return from main(). | ||
* @param async Specifies whether our termination will be non-blocking. | ||
*/ | ||
void | ||
exit_sunshine(int exit_code, bool async) { | ||
// Store the exit code of the first exit_sunshine() call | ||
int zero = 0; | ||
desired_exit_code.compare_exchange_strong(zero, exit_code); | ||
|
||
// Raise SIGINT to start termination | ||
std::raise(SIGINT); | ||
|
||
// Termination will happen asynchronously, but the caller may | ||
// have wanted synchronous behavior. | ||
while (!async) { | ||
std::this_thread::sleep_for(1s); | ||
} | ||
} | ||
|
||
/** | ||
* @brief Gets the argv array passed to main(). | ||
*/ | ||
char ** | ||
get_argv() { | ||
return argv; | ||
} | ||
} // namespace lifetime | ||
|
||
#ifdef _WIN32 | ||
/** | ||
* @brief Checks if NVIDIA's GameStream software is running. | ||
* @return `true` if GameStream is enabled. | ||
*/ | ||
bool | ||
is_gamestream_enabled() { | ||
DWORD enabled; | ||
DWORD size = sizeof(enabled); | ||
return RegGetValueW( | ||
HKEY_LOCAL_MACHINE, | ||
L"SOFTWARE\\NVIDIA Corporation\\NvStream", | ||
L"EnableStreaming", | ||
RRF_RT_REG_DWORD, | ||
nullptr, | ||
&enabled, | ||
&size) == ERROR_SUCCESS && | ||
enabled != 0; | ||
} | ||
|
||
namespace service_ctrl { | ||
class service_controller { | ||
public: | ||
/** | ||
* @brief Constructor for service_controller class. | ||
* @param service_desired_access SERVICE_* desired access flags. | ||
*/ | ||
service_controller(DWORD service_desired_access) { | ||
scm_handle = OpenSCManagerA(nullptr, nullptr, SC_MANAGER_CONNECT); | ||
if (!scm_handle) { | ||
auto winerr = GetLastError(); | ||
BOOST_LOG(error) << "OpenSCManager() failed: "sv << winerr; | ||
return; | ||
} | ||
|
||
service_handle = OpenServiceA(scm_handle, "SunshineService", service_desired_access); | ||
if (!service_handle) { | ||
auto winerr = GetLastError(); | ||
BOOST_LOG(error) << "OpenService() failed: "sv << winerr; | ||
return; | ||
} | ||
} | ||
|
||
~service_controller() { | ||
if (service_handle) { | ||
CloseServiceHandle(service_handle); | ||
} | ||
|
||
if (scm_handle) { | ||
CloseServiceHandle(scm_handle); | ||
} | ||
} | ||
|
||
/** | ||
* @brief Asynchronously starts the Sunshine service. | ||
*/ | ||
bool | ||
start_service() { | ||
if (!service_handle) { | ||
return false; | ||
} | ||
|
||
if (!StartServiceA(service_handle, 0, nullptr)) { | ||
auto winerr = GetLastError(); | ||
if (winerr != ERROR_SERVICE_ALREADY_RUNNING) { | ||
BOOST_LOG(error) << "StartService() failed: "sv << winerr; | ||
return false; | ||
} | ||
} | ||
|
||
return true; | ||
} | ||
|
||
/** | ||
* @brief Query the service status. | ||
* @param status The SERVICE_STATUS struct to populate. | ||
*/ | ||
bool | ||
query_service_status(SERVICE_STATUS &status) { | ||
if (!service_handle) { | ||
return false; | ||
} | ||
|
||
if (!QueryServiceStatus(service_handle, &status)) { | ||
auto winerr = GetLastError(); | ||
BOOST_LOG(error) << "QueryServiceStatus() failed: "sv << winerr; | ||
return false; | ||
} | ||
|
||
return true; | ||
} | ||
|
||
private: | ||
SC_HANDLE scm_handle = NULL; | ||
SC_HANDLE service_handle = NULL; | ||
}; | ||
|
||
/** | ||
* @brief Check if the service is running. | ||
* | ||
* EXAMPLES: | ||
* ```cpp | ||
* is_service_running(); | ||
* ``` | ||
*/ | ||
bool | ||
is_service_running() { | ||
service_controller sc { SERVICE_QUERY_STATUS }; | ||
|
||
SERVICE_STATUS status; | ||
if (!sc.query_service_status(status)) { | ||
return false; | ||
} | ||
|
||
return status.dwCurrentState == SERVICE_RUNNING; | ||
} | ||
|
||
/** | ||
* @brief Start the service and wait for startup to complete. | ||
* | ||
* EXAMPLES: | ||
* ```cpp | ||
* start_service(); | ||
* ``` | ||
*/ | ||
bool | ||
start_service() { | ||
service_controller sc { SERVICE_QUERY_STATUS | SERVICE_START }; | ||
|
||
std::cout << "Starting Sunshine..."sv; | ||
|
||
// This operation is asynchronous, so we must wait for it to complete | ||
if (!sc.start_service()) { | ||
return false; | ||
} | ||
|
||
SERVICE_STATUS status; | ||
do { | ||
Sleep(1000); | ||
std::cout << '.'; | ||
} while (sc.query_service_status(status) && status.dwCurrentState == SERVICE_START_PENDING); | ||
|
||
if (status.dwCurrentState != SERVICE_RUNNING) { | ||
BOOST_LOG(error) << SERVICE_NAME " failed to start: "sv << status.dwWin32ExitCode; | ||
return false; | ||
} | ||
|
||
std::cout << std::endl; | ||
return true; | ||
} | ||
|
||
/** | ||
* @brief Wait for the UI to be ready after Sunshine startup. | ||
* | ||
* EXAMPLES: | ||
* ```cpp | ||
* wait_for_ui_ready(); | ||
* ``` | ||
*/ | ||
bool | ||
wait_for_ui_ready() { | ||
std::cout << "Waiting for Web UI to be ready..."; | ||
|
||
// Wait up to 30 seconds for the web UI to start | ||
for (int i = 0; i < 30; i++) { | ||
PMIB_TCPTABLE tcp_table = nullptr; | ||
ULONG table_size = 0; | ||
ULONG err; | ||
|
||
auto fg = util::fail_guard([&tcp_table]() { | ||
free(tcp_table); | ||
}); | ||
|
||
do { | ||
// Query all open TCP sockets to look for our web UI port | ||
err = GetTcpTable(tcp_table, &table_size, false); | ||
if (err == ERROR_INSUFFICIENT_BUFFER) { | ||
free(tcp_table); | ||
tcp_table = (PMIB_TCPTABLE) malloc(table_size); | ||
} | ||
} while (err == ERROR_INSUFFICIENT_BUFFER); | ||
|
||
if (err != NO_ERROR) { | ||
BOOST_LOG(error) << "Failed to query TCP table: "sv << err; | ||
return false; | ||
} | ||
|
||
uint16_t port_nbo = htons(net::map_port(confighttp::PORT_HTTPS)); | ||
for (DWORD i = 0; i < tcp_table->dwNumEntries; i++) { | ||
auto &entry = tcp_table->table[i]; | ||
|
||
// Look for our port in the listening state | ||
if (entry.dwLocalPort == port_nbo && entry.dwState == MIB_TCP_STATE_LISTEN) { | ||
std::cout << std::endl; | ||
return true; | ||
} | ||
} | ||
|
||
Sleep(1000); | ||
std::cout << '.'; | ||
} | ||
|
||
std::cout << "timed out"sv << std::endl; | ||
return false; | ||
} | ||
} // namespace service_ctrl | ||
#endif |
Oops, something went wrong.