Skip to content

Commit

Permalink
refactor(main): move remaining entry related code
Browse files Browse the repository at this point in the history
  • Loading branch information
ReenigneArcher committed Feb 10, 2024
1 parent 78ed91a commit ba2906b
Show file tree
Hide file tree
Showing 29 changed files with 475 additions and 422 deletions.
2 changes: 2 additions & 0 deletions cmake/compile_definitions/common.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ set(SUNSHINE_TARGET_FILES
"${CMAKE_SOURCE_DIR}/src/uuid.h"
"${CMAKE_SOURCE_DIR}/src/config.h"
"${CMAKE_SOURCE_DIR}/src/config.cpp"
"${CMAKE_SOURCE_DIR}/src/entry_handler.cpp"
"${CMAKE_SOURCE_DIR}/src/entry_handler.h"
"${CMAKE_SOURCE_DIR}/src/file_handler.cpp"
"${CMAKE_SOURCE_DIR}/src/file_handler.h"
"${CMAKE_SOURCE_DIR}/src/logging.cpp"
Expand Down
5 changes: 5 additions & 0 deletions docs/source/source_code/src/entry_handler.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
entry_handler
=============

.. doxygenfile:: entry_handler.h
:allow-dot-graphs:
2 changes: 1 addition & 1 deletion src/audio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@

#include "audio.h"
#include "config.h"
#include "entry_handler.h"
#include "logging.h"
#include "main.h"
#include "thread_safe.h"
#include "utility.h"

Expand Down
3 changes: 2 additions & 1 deletion src/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@
#include <boost/property_tree/ptree.hpp>

#include "config.h"
#include "entry_handler.h"
#include "file_handler.h"
#include "logging.h"
#include "main.h"
#include "nvhttp.h"
#include "rtsp.h"
#include "thread_pool.h"
#include "utility.h"

#include "platform/common.h"
Expand Down
2 changes: 1 addition & 1 deletion src/confighttp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@
#include "config.h"
#include "confighttp.h"
#include "crypto.h"
#include "entry_handler.h"
#include "file_handler.h"
#include "httpcommon.h"
#include "logging.h"
#include "main.h"
#include "network.h"
#include "nvhttp.h"
#include "platform/common.h"
Expand Down
343 changes: 343 additions & 0 deletions src/entry_handler.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,343 @@
/**
* @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);
}

#ifdef _WIN32
// Define global singleton used for NVIDIA control panel modifications
nvprefs::nvprefs_interface nvprefs_instance;
#endif

namespace args {
int
creds(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;
}

int
help(const char *name, int argc, char *argv[]) {
print_help(name);
return 0;
}

int
version(const char *name, int argc, char *argv[]) {
std::cout << PROJECT_NAME << " version: v" << PROJECT_VER << std::endl;
return 0;
}

#ifdef _WIN32
int
restore_nvprefs_undo(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;
}
#endif
} // namespace args

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
Loading

0 comments on commit ba2906b

Please sign in to comment.