refactor(main): move remaining entry related code (#2127)
This commit is contained in:
353
src/main.cpp
353
src/main.cpp
@@ -5,31 +5,22 @@
|
||||
|
||||
// standard includes
|
||||
#include <csignal>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
// lib includes
|
||||
#include <boost/log/attributes/clock.hpp>
|
||||
#include <boost/log/common.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
#include <boost/log/sinks.hpp>
|
||||
#include <boost/log/sources/severity_logger.hpp>
|
||||
|
||||
// local includes
|
||||
#include "config.h"
|
||||
#include "confighttp.h"
|
||||
#include "entry_handler.h"
|
||||
#include "globals.h"
|
||||
#include "httpcommon.h"
|
||||
#include "logging.h"
|
||||
#include "main.h"
|
||||
#include "network.h"
|
||||
#include "nvhttp.h"
|
||||
#include "platform/common.h"
|
||||
#include "process.h"
|
||||
#include "rtsp.h"
|
||||
#include "system_tray.h"
|
||||
#include "thread_pool.h"
|
||||
#include "upnp.h"
|
||||
#include "version.h"
|
||||
#include "video.h"
|
||||
@@ -37,26 +28,11 @@
|
||||
extern "C" {
|
||||
#include <libavutil/log.h>
|
||||
#include <rs.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <iphlpapi.h>
|
||||
#endif
|
||||
}
|
||||
|
||||
safe::mail_t mail::man;
|
||||
|
||||
using namespace std::literals;
|
||||
namespace bl = boost::log;
|
||||
|
||||
#ifdef _WIN32
|
||||
// Define global singleton used for NVIDIA control panel modifications
|
||||
nvprefs::nvprefs_interface nvprefs_instance;
|
||||
#endif
|
||||
|
||||
thread_pool_util::ThreadPool task_pool;
|
||||
|
||||
bool display_cursor = true;
|
||||
|
||||
struct NoDelete {
|
||||
void
|
||||
operator()(void *) {}
|
||||
@@ -64,309 +40,6 @@ struct NoDelete {
|
||||
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", int)
|
||||
|
||||
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
|
||||
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 {
|
||||
static char **argv;
|
||||
static 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
|
||||
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
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @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);
|
||||
}
|
||||
|
||||
std::map<int, std::function<void()>> signal_handlers;
|
||||
void
|
||||
on_signal_forwarder(int sig) {
|
||||
@@ -381,26 +54,12 @@ on_signal(int sig, FN &&fn) {
|
||||
std::signal(sig, on_signal_forwarder);
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
std::map<std::string_view, std::function<int(const char *name, int argc, char **argv)>> cmd_to_func {
|
||||
{ "creds"sv, gen_creds::entry },
|
||||
{ "help"sv, help::entry },
|
||||
{ "version"sv, version::entry },
|
||||
{ "creds"sv, args::creds },
|
||||
{ "help"sv, args::help },
|
||||
{ "version"sv, args::version },
|
||||
#ifdef _WIN32
|
||||
{ "restore-nvprefs-undo"sv, restore_nvprefs_undo::entry },
|
||||
{ "restore-nvprefs-undo"sv, args::restore_nvprefs_undo },
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user