This commit is contained in:
pigeatgarlic 2023-10-26 19:10:58 +07:00
parent f1d509adef
commit b6c1306d43
13 changed files with 71 additions and 1741 deletions

View File

@ -27,7 +27,6 @@ include_directories(SYSTEM third-party/nv-codec-headers/include)
file(GLOB NVENC_SOURCES CONFIGURE_DEPENDS "src/nvenc/*.cpp" "src/nvenc/*.h")
list(APPEND PLATFORM_TARGET_FILES ${NVENC_SOURCES})
configure_file(src/version.h.in version.h @ONLY)
include_directories(${CMAKE_CURRENT_BINARY_DIR})
set(SUNSHINE_TARGET_FILES
@ -47,10 +46,6 @@ set(SUNSHINE_TARGET_FILES
src/main.h
src/crypto.cpp
src/crypto.h
src/nvhttp.cpp
src/nvhttp.h
src/httpcommon.cpp
src/httpcommon.h
src/rtsp.cpp
src/rtsp.h
src/stream.cpp

View File

@ -37,7 +37,6 @@ configure_file(src/platform/windows/windows.rs.in windows.rc @ONLY)
set(PLATFORM_TARGET_FILES
"${CMAKE_CURRENT_BINARY_DIR}/windows.rc"
src/platform/windows/publish.cpp
src/platform/windows/misc.h
src/platform/windows/misc.cpp
src/platform/windows/input.cpp

View File

@ -2,6 +2,7 @@
# this file will also load platform specific macros
add_executable(sunshine ${SUNSHINE_TARGET_FILES})
add_library(sunshinelib SHARED ${SUNSHINE_TARGET_FILES})
# platform specific target definitions
if(WIN32)
@ -22,6 +23,12 @@ if(NOT DEFINED CMAKE_CUDA_STANDARD)
set(CMAKE_CUDA_STANDARD_REQUIRED ON)
endif()
target_link_libraries(sunshinelib ${SUNSHINE_EXTERNAL_LIBRARIES} ${EXTRA_LIBS})
target_compile_definitions(sunshinelib PUBLIC ${SUNSHINE_DEFINITIONS})
set_target_properties(sunshinelib PROPERTIES CXX_STANDARD 17
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR})
target_link_libraries(sunshine ${SUNSHINE_EXTERNAL_LIBRARIES} ${EXTRA_LIBS})
target_compile_definitions(sunshine PUBLIC ${SUNSHINE_DEFINITIONS})
set_target_properties(sunshine PROPERTIES CXX_STANDARD 17
@ -33,3 +40,4 @@ foreach(flag IN LISTS SUNSHINE_COMPILE_OPTIONS)
endforeach()
target_compile_options(sunshine PRIVATE $<$<COMPILE_LANGUAGE:CXX>:${SUNSHINE_COMPILE_OPTIONS}>;$<$<COMPILE_LANGUAGE:CUDA>:${SUNSHINE_COMPILE_OPTIONS_CUDA};-std=c++17>) # cmake-lint: disable=C0301
target_compile_options(sunshinelib PRIVATE $<$<COMPILE_LANGUAGE:CXX>:${SUNSHINE_COMPILE_OPTIONS}>;$<$<COMPILE_LANGUAGE:CUDA>:${SUNSHINE_COMPILE_OPTIONS_CUDA};-std=c++17>) # cmake-lint: disable=C0301

View File

@ -16,7 +16,6 @@
#include "config.h"
#include "main.h"
#include "nvhttp.h"
#include "rtsp.h"
#include "utility.h"
@ -379,30 +378,6 @@ namespace config {
1 // channels
};
nvhttp_t nvhttp {
"lan", // origin web manager
PRIVATE_KEY_FILE,
CERTIFICATE_FILE,
boost::asio::ip::host_name(), // sunshine_name,
"sunshine_state.json"s, // file_state
{}, // external_ip
{
"352x240"s,
"480x360"s,
"858x480"s,
"1280x720"s,
"1920x1080"s,
"2560x1080"s,
"3440x1440"s,
"1920x1200"s,
"3840x2160"s,
"3840x1600"s,
}, // supported resolutions
{ 10, 30, 60, 90, 120 }, // supported fps
};
input_t input {
{
@ -525,32 +500,6 @@ namespace config {
std::make_pair(to_string(begin, end_name), to_string(begin_val, endl)));
}
std::unordered_map<std::string, std::string>
parse_config(const std::string_view &file_content) {
std::unordered_map<std::string, std::string> vars;
auto pos = std::begin(file_content);
auto end = std::end(file_content);
while (pos < end) {
// auto newline = std::find_if(pos, end, [](auto ch) { return ch == '\n' || ch == '\r'; });
TUPLE_2D(endl, var, parse_option(pos, end));
pos = endl;
if (pos != end) {
pos += (*pos == '\r') ? 2 : 1;
}
if (!var) {
continue;
}
vars.emplace(std::move(*var));
}
return vars;
}
void
string_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, std::string &input) {
auto it = vars.find(name);
@ -977,27 +926,17 @@ namespace config {
string_f(vars, "adapter_name", video.adapter_name);
string_f(vars, "output_name", video.output_name);
path_f(vars, "pkey", nvhttp.pkey);
path_f(vars, "cert", nvhttp.cert);
string_f(vars, "sunshine_name", nvhttp.sunshine_name);
path_f(vars, "log_path", config::sunshine.log_file);
path_f(vars, "file_state", nvhttp.file_state);
// Must be run after "file_state"
config::sunshine.credentials_file = config::nvhttp.file_state;
path_f(vars, "credentials_file", config::sunshine.credentials_file);
string_f(vars, "external_ip", nvhttp.external_ip);
list_string_f(vars, "resolutions"s, nvhttp.resolutions);
list_int_f(vars, "fps"s, nvhttp.fps);
list_prep_cmd_f(vars, "global_prep_cmd", config::sunshine.prep_cmds);
string_f(vars, "audio_sink", audio.sink);
string_f(vars, "virtual_sink", audio.virtual_sink);
bool_f(vars, "install_steam_audio_drivers", audio.install_steam_drivers);
string_restricted_f(vars, "origin_web_ui_allowed", nvhttp.origin_web_ui_allowed, { "pc"sv, "lan"sv, "wan"sv });
int to = -1;
int_between_f(vars, "ping_timeout", to, { -1, std::numeric_limits<int>::max() });
if (to != -1) {
@ -1049,7 +988,6 @@ namespace config {
bool_f(vars, "always_send_scancodes", input.always_send_scancodes);
int port = sunshine.port;
int_between_f(vars, "port"s, port, { 1024 + nvhttp::PORT_HTTPS, 65535 - rtsp_stream::RTSP_SETUP_PORT });
sunshine.port = (std::uint16_t) port;
string_restricted_f(vars, "address_family", sunshine.address_family, { "ipv4"sv, "both"sv });
@ -1108,111 +1046,4 @@ namespace config {
}
}
}
int
parse(int argc, char *argv[]) {
std::unordered_map<std::string, std::string> cmd_vars;
#ifdef _WIN32
bool shortcut_launch = false;
bool service_admin_launch = false;
#endif
for (auto x = 1; x < argc; ++x) {
auto line = argv[x];
if (line == "--help"sv) {
print_help(*argv);
return 1;
}
#ifdef _WIN32
else if (line == "--shortcut"sv) {
shortcut_launch = true;
}
else if (line == "--shortcut-admin"sv) {
service_admin_launch = true;
}
#endif
else if (*line == '-') {
if (*(line + 1) == '-') {
sunshine.cmd.name = line + 2;
sunshine.cmd.argc = argc - x - 1;
sunshine.cmd.argv = argv + x + 1;
break;
}
if (apply_flags(line + 1)) {
print_help(*argv);
return -1;
}
}
else {
auto line_end = line + strlen(line);
auto pos = std::find(line, line_end, '=');
if (pos == line_end) {
sunshine.config_file = line;
}
else {
TUPLE_EL(var, 1, parse_option(line, line_end));
if (!var) {
print_help(*argv);
return -1;
}
TUPLE_EL_REF(name, 0, *var);
auto it = cmd_vars.find(name);
if (it != std::end(cmd_vars)) {
cmd_vars.erase(it);
}
cmd_vars.emplace(std::move(*var));
}
}
}
bool config_loaded = false;
try {
// Create appdata folder if it does not exist
if (!boost::filesystem::exists(platf::appdata().string())) {
boost::filesystem::create_directories(platf::appdata().string());
}
// Create empty config file if it does not exist
if (!fs::exists(sunshine.config_file)) {
std::ofstream { sunshine.config_file };
}
// Read config file
auto vars = parse_config(read_file(sunshine.config_file.c_str()));
for (auto &[name, value] : cmd_vars) {
vars.insert_or_assign(std::move(name), std::move(value));
}
// Apply the config. Note: This will try to create any paths
// referenced in the config, so we may receive exceptions if
// the path is incorrect or inaccessible.
apply_config(std::move(vars));
config_loaded = true;
}
catch (const std::filesystem::filesystem_error &err) {
BOOST_LOG(fatal) << "Failed to apply config: "sv << err.what();
}
catch (const boost::filesystem::filesystem_error &err) {
BOOST_LOG(fatal) << "Failed to apply config: "sv << err.what();
}
if (!config_loaded) {
#ifdef _WIN32
BOOST_LOG(fatal) << "To relaunch Sunshine successfully, use the shortcut in the Start Menu. Do not run Sunshine.exe manually."sv;
std::this_thread::sleep_for(10s);
#endif
return -1;
}
return 0;
}
} // namespace config

View File

@ -171,9 +171,4 @@ namespace config {
extern nvhttp_t nvhttp;
extern input_t input;
extern sunshine_t sunshine;
int
parse(int argc, char *argv[]);
std::unordered_map<std::string, std::string>
parse_config(const std::string_view &file_content);
} // namespace config

View File

@ -1,247 +0,0 @@
/**
* @file src/httpcommon.cpp
* @brief todo
*/
#define BOOST_BIND_GLOBAL_PLACEHOLDERS
#include "process.h"
#include <filesystem>
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <boost/asio/ssl/context.hpp>
#include <Simple-Web-Server/server_http.hpp>
#include <Simple-Web-Server/server_https.hpp>
#include <boost/asio/ssl/context_base.hpp>
#include <curl/curl.h>
#include "config.h"
#include "crypto.h"
#include "httpcommon.h"
#include "main.h"
#include "network.h"
#include "nvhttp.h"
#include "platform/common.h"
#include "rtsp.h"
#include "utility.h"
#include "uuid.h"
namespace http {
using namespace std::literals;
namespace fs = std::filesystem;
namespace pt = boost::property_tree;
int
reload_user_creds(const std::string &file);
bool
user_creds_exist(const std::string &file);
std::string unique_id;
net::net_e origin_web_ui_allowed;
int
init() {
bool clean_slate = config::sunshine.flags[config::flag::FRESH_STATE];
origin_web_ui_allowed = net::from_enum_string(config::nvhttp.origin_web_ui_allowed);
if (clean_slate) {
unique_id = uuid_util::uuid_t::generate().string();
auto dir = std::filesystem::temp_directory_path() / "Sunshine"sv;
config::nvhttp.cert = (dir / ("cert-"s + unique_id)).string();
config::nvhttp.pkey = (dir / ("pkey-"s + unique_id)).string();
}
if (!fs::exists(config::nvhttp.pkey) || !fs::exists(config::nvhttp.cert)) {
if (create_creds(config::nvhttp.pkey, config::nvhttp.cert)) {
return -1;
}
}
if (user_creds_exist(config::sunshine.credentials_file)) {
if (reload_user_creds(config::sunshine.credentials_file)) return -1;
}
else {
BOOST_LOG(info) << "Open the Web UI to set your new username and password and getting started";
}
return 0;
}
int
save_user_creds(const std::string &file, const std::string &username, const std::string &password, bool run_our_mouth) {
pt::ptree outputTree;
if (fs::exists(file)) {
try {
pt::read_json(file, outputTree);
}
catch (std::exception &e) {
BOOST_LOG(error) << "Couldn't read user credentials: "sv << e.what();
return -1;
}
}
auto salt = crypto::rand_alphabet(16);
outputTree.put("username", username);
outputTree.put("salt", salt);
outputTree.put("password", util::hex(crypto::hash(password + salt)).to_string());
try {
pt::write_json(file, outputTree);
}
catch (std::exception &e) {
BOOST_LOG(error) << "error writing to the credentials file, perhaps try this again as an administrator? Details: "sv << e.what();
return -1;
}
BOOST_LOG(info) << "New credentials have been created"sv;
return 0;
}
bool
user_creds_exist(const std::string &file) {
if (!fs::exists(file)) {
return false;
}
pt::ptree inputTree;
try {
pt::read_json(file, inputTree);
return inputTree.find("username") != inputTree.not_found() &&
inputTree.find("password") != inputTree.not_found() &&
inputTree.find("salt") != inputTree.not_found();
}
catch (std::exception &e) {
BOOST_LOG(error) << "validating user credentials: "sv << e.what();
}
return false;
}
int
reload_user_creds(const std::string &file) {
pt::ptree inputTree;
try {
pt::read_json(file, inputTree);
config::sunshine.username = inputTree.get<std::string>("username");
config::sunshine.password = inputTree.get<std::string>("password");
config::sunshine.salt = inputTree.get<std::string>("salt");
}
catch (std::exception &e) {
BOOST_LOG(error) << "loading user credentials: "sv << e.what();
return -1;
}
return 0;
}
int
create_creds(const std::string &pkey, const std::string &cert) {
fs::path pkey_path = pkey;
fs::path cert_path = cert;
auto creds = crypto::gen_creds("Sunshine Gamestream Host"sv, 2048);
auto pkey_dir = pkey_path;
auto cert_dir = cert_path;
pkey_dir.remove_filename();
cert_dir.remove_filename();
std::error_code err_code {};
fs::create_directories(pkey_dir, err_code);
if (err_code) {
BOOST_LOG(error) << "Couldn't create directory ["sv << pkey_dir << "] :"sv << err_code.message();
return -1;
}
fs::create_directories(cert_dir, err_code);
if (err_code) {
BOOST_LOG(error) << "Couldn't create directory ["sv << cert_dir << "] :"sv << err_code.message();
return -1;
}
if (write_file(pkey.c_str(), creds.pkey)) {
BOOST_LOG(error) << "Couldn't open ["sv << config::nvhttp.pkey << ']';
return -1;
}
if (write_file(cert.c_str(), creds.x509)) {
BOOST_LOG(error) << "Couldn't open ["sv << config::nvhttp.cert << ']';
return -1;
}
fs::permissions(pkey_path,
fs::perms::owner_read | fs::perms::owner_write,
fs::perm_options::replace, err_code);
if (err_code) {
BOOST_LOG(error) << "Couldn't change permissions of ["sv << config::nvhttp.pkey << "] :"sv << err_code.message();
return -1;
}
fs::permissions(cert_path,
fs::perms::owner_read | fs::perms::group_read | fs::perms::others_read | fs::perms::owner_write,
fs::perm_options::replace, err_code);
if (err_code) {
BOOST_LOG(error) << "Couldn't change permissions of ["sv << config::nvhttp.cert << "] :"sv << err_code.message();
return -1;
}
return 0;
}
bool
download_file(const std::string &url, const std::string &file) {
CURL *curl = curl_easy_init();
if (!curl) {
BOOST_LOG(error) << "Couldn't create CURL instance";
return false;
}
FILE *fp = fopen(file.c_str(), "wb");
if (!fp) {
BOOST_LOG(error) << "Couldn't open ["sv << file << ']';
curl_easy_cleanup(curl);
return false;
}
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
#ifdef _WIN32
curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA);
#endif
CURLcode result = curl_easy_perform(curl);
if (result != CURLE_OK) {
BOOST_LOG(error) << "Couldn't download ["sv << url << ", code:" << result << ']';
}
curl_easy_cleanup(curl);
fclose(fp);
return result == CURLE_OK;
}
std::string
url_escape(const std::string &url) {
CURL *curl = curl_easy_init();
char *string = curl_easy_escape(curl, url.c_str(), url.length());
std::string result(string);
curl_free(string);
curl_easy_cleanup(curl);
return result;
}
std::string
url_get_host(const std::string &url) {
CURLU *curlu = curl_url();
curl_url_set(curlu, CURLUPART_URL, url.c_str(), url.length());
char *host;
if (curl_url_get(curlu, CURLUPART_HOST, &host, 0) != CURLUE_OK) {
curl_url_cleanup(curlu);
return "";
}
std::string result(host);
curl_free(host);
curl_url_cleanup(curlu);
return result;
}
} // namespace http

View File

@ -1,35 +0,0 @@
/**
* @file src/httpcommon.h
* @brief todo
*/
#pragma once
#include "network.h"
#include "thread_safe.h"
namespace http {
int
init();
int
create_creds(const std::string &pkey, const std::string &cert);
int
save_user_creds(
const std::string &file,
const std::string &username,
const std::string &password,
bool run_our_mouth = false);
int
reload_user_creds(const std::string &file);
bool
download_file(const std::string &url, const std::string &file);
std::string
url_escape(const std::string &url);
std::string
url_get_host(const std::string &url);
extern std::string unique_id;
extern net::net_e origin_web_ui_allowed;
} // namespace http

View File

@ -23,7 +23,6 @@
#include "platform/common.h"
#include "rtsp.h"
#include "thread_pool.h"
#include "version.h"
#include "video.h"
#include "stream.h"
@ -68,51 +67,6 @@ struct NoDelete {
BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", int)
/**
* @brief Print help to stdout.
* @param name The name of the program.
*
* EXAMPLES:
* ```cpp
* print_help("sunshine");
* ```
*/
void
print_help(const char *name) {
std::cout
<< "Usage: "sv << name << " [options] [/path/to/configuration_file] [--cmd]"sv << std::endl
<< " Any configurable option can be overwritten with: \"name=value\""sv << std::endl
<< std::endl
<< " Note: The configuration will be created if it doesn't exist."sv << std::endl
<< std::endl
<< " --help | print help"sv << std::endl
<< " --creds username password | set user credentials for the Web manager"sv << std::endl
<< " --version | print the version of sunshine"sv << std::endl
<< std::endl
<< " flags"sv << std::endl
<< " -0 | Read PIN from stdin"sv << std::endl
<< " -1 | Do not load previously saved state and do retain any state after shutdown"sv << std::endl
<< " | Effectively starting as if for the first time without overwriting any pairings with your devices"sv << std::endl
<< " -2 | Force replacement of headers in video stream"sv << std::endl
<< " -p | Enable/Disable UPnP"sv << std::endl
<< std::endl;
}
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 {
@ -145,23 +99,31 @@ log_flush() {
sink->flush();
}
std::map<int, std::function<void()>> signal_handlers;
void
on_signal_forwarder(int sig) {
signal_handlers.at(sig)();
/**
* @brief Map a specified port based on the base port.
* @param port The port to map as a difference from the base port.
* @return `std:uint16_t` : The mapped port number.
*
* EXAMPLES:
* ```cpp
* std::uint16_t mapped_port = map_port(1);
* ```
*/
std::uint16_t
map_port(int port) {
// calculate the port from the config port
auto mapped_port = (std::uint16_t)((int) config::sunshine.port + port);
// Ensure port is in the range of 1024-65535
if (mapped_port < 1024 || mapped_port > 65535) {
BOOST_LOG(warning) << "Port out of range: "sv << mapped_port;
}
// TODO: Ensure port is not already in use by another application
return mapped_port;
}
template <class FN>
void
on_signal(int sig, FN &&fn) {
signal_handlers.emplace(sig, std::forward<FN>(fn));
std::signal(sig, on_signal_forwarder);
}
@ -190,20 +152,10 @@ videoBroadcastThreadmain() {
shutdown_event->raise(true);
}
/**
* @brief Main application entry point.
* @param argc The number of arguments.
* @param argv The arguments.
*
* EXAMPLES:
* ```cpp
* main(1, const char* args[] = {"sunshine", nullptr});
* ```
*/
int
main(int argc, char *argv[]) {
task_pool_util::TaskPool::task_id_t force_shutdown = nullptr;
void Init() {
#ifdef _WIN32
// Switch default C standard library locale to UTF-8 on Windows 10 1803+
setlocale(LC_ALL, ".UTF-8");
@ -211,12 +163,8 @@ main(int argc, char *argv[]) {
// Use UTF-8 conversion for the default C++ locale (used by boost::log)
std::locale::global(std::locale(std::locale(), new std::codecvt_utf8<wchar_t>));
mail::man = std::make_shared<safe::mail_raw_t>();
if (config::parse(argc, argv)) {
return 0;
}
if (config::sunshine.min_log_level >= 1) {
av_log_set_level(AV_LOG_QUIET);
@ -315,42 +263,11 @@ main(int argc, char *argv[]) {
SetProcessShutdownParameters(0x100, SHUTDOWN_NORETRY);
#endif
BOOST_LOG(info) << PROJECT_NAME << " version: " << PROJECT_VER << std::endl;
task_pool.start(1);
// Create signal handler after logging has been initialized
auto shutdown_event = mail::man->event<bool>(mail::shutdown);
on_signal(SIGINT, [&force_shutdown, shutdown_event]() {
BOOST_LOG(info) << "Interrupt handler called"sv;
auto task = []() {
BOOST_LOG(fatal) << "10 seconds passed, yet Sunshine's still running: Forcing shutdown"sv;
log_flush();
std::abort();
};
force_shutdown = task_pool.pushDelayed(task, 10s).task_id;
shutdown_event->raise(true);
});
on_signal(SIGTERM, [&force_shutdown, shutdown_event]() {
BOOST_LOG(info) << "Terminate handler called"sv;
auto task = []() {
BOOST_LOG(fatal) << "10 seconds passed, yet Sunshine's still running: Forcing shutdown"sv;
log_flush();
std::abort();
};
force_shutdown = task_pool.pushDelayed(task, 10s).task_id;
shutdown_event->raise(true);
});
// If any of the following fail, we log an error and continue event though sunshine will not function correctly.
// This allows access to the UI to fix configuration problems or view the logs.
auto deinit_guard = platf::init();
if (!deinit_guard) {
BOOST_LOG(error) << "Platform failed to initialize"sv;
@ -361,9 +278,31 @@ main(int argc, char *argv[]) {
if (video::probe_encoders()) {
BOOST_LOG(error) << "Video failed to find working encoder"sv;
}
}
void DeInit(){
#ifdef WIN32
// Restore global NVIDIA control panel settings
if (nvprefs_instance.owning_undo_file() && nvprefs_instance.load()) {
nvprefs_instance.restore_global_profile();
nvprefs_instance.unload();
}
#endif
}
/**
* @brief Main application entry point.
* @param argc The number of arguments.
* @param argv The arguments.
*
* EXAMPLES:
* ```cpp
* main(1, const char* args[] = {"sunshine", nullptr});
* ```
*/
int
main(int argc, char *argv[]) {
Init();
auto video = std::thread { videoBroadcastThreadmain };
@ -376,97 +315,8 @@ main(int argc, char *argv[]) {
shutdown_event->view();
task_pool.stop();
task_pool.join();
#ifdef WIN32
// Restore global NVIDIA control panel settings
if (nvprefs_instance.owning_undo_file() && nvprefs_instance.load()) {
nvprefs_instance.restore_global_profile();
nvprefs_instance.unload();
}
#endif
// Create signal handler after logging has been initialized
mail::man->event<bool>(mail::shutdown)->view();
DeInit();
return 0;
}
/**
* @brief Read a file to string.
* @param path The path of the file.
* @return `std::string` : The contents of the file.
*
* EXAMPLES:
* ```cpp
* std::string contents = read_file("path/to/file");
* ```
*/
std::string
read_file(const char *path) {
if (!std::filesystem::exists(path)) {
BOOST_LOG(debug) << "Missing file: " << path;
return {};
}
std::ifstream in(path);
std::string input;
std::string base64_cert;
while (!in.eof()) {
std::getline(in, input);
base64_cert += input + '\n';
}
return base64_cert;
}
/**
* @brief Writes a file.
* @param path The path of the file.
* @param contents The contents to write.
* @return `int` : `0` on success, `-1` on failure.
*
* EXAMPLES:
* ```cpp
* int write_status = write_file("path/to/file", "file contents");
* ```
*/
int
write_file(const char *path, const std::string_view &contents) {
std::ofstream out(path);
if (!out.is_open()) {
return -1;
}
out << contents;
return 0;
}
/**
* @brief Map a specified port based on the base port.
* @param port The port to map as a difference from the base port.
* @return `std:uint16_t` : The mapped port number.
*
* EXAMPLES:
* ```cpp
* std::uint16_t mapped_port = map_port(1);
* ```
*/
std::uint16_t
map_port(int port) {
// calculate the port from the config port
auto mapped_port = (std::uint16_t)((int) config::sunshine.port + port);
// Ensure port is in the range of 1024-65535
if (mapped_port < 1024 || mapped_port > 65535) {
BOOST_LOG(warning) << "Port out of range: "sv << mapped_port;
}
// TODO: Ensure port is not already in use by another application
return mapped_port;
}

View File

@ -38,18 +38,8 @@ int
main(int argc, char *argv[]);
void
log_flush();
void
print_help(const char *name);
std::string
read_file(const char *path);
int
write_file(const char *path, const std::string_view &contents);
std::uint16_t
map_port(int port);
void
launch_ui();
void
launch_ui_with_path(std::string path);
// namespaces
namespace mail {

File diff suppressed because it is too large Load Diff

View File

@ -1,49 +0,0 @@
/**
* @file src/nvhttp.h
* @brief todo
*/
// macros
#pragma once
// standard includes
#include <string>
// local includes
#include "thread_safe.h"
/**
* @brief This namespace contains all the functions and variables related to the nvhttp (GameStream) server.
*/
namespace nvhttp {
/**
* @brief The protocol version.
* @details The version of the GameStream protocol we are mocking.
* @note The negative 4th number indicates to Moonlight that this is Sunshine.
*/
constexpr auto VERSION = "7.1.431.-1";
/**
* @brief The GFE version we are replicating.
*/
constexpr auto GFE_VERSION = "3.23.0.74";
/**
* @brief The HTTP port, as a difference from the config port.
*/
constexpr auto PORT_HTTP = 0;
/**
* @brief The HTTPS port, as a difference from the config port.
*/
constexpr auto PORT_HTTPS = -5;
// functions
void
start();
bool
pin(std::string pin);
void
erase_all_clients();
} // namespace nvhttp

View File

@ -1674,15 +1674,20 @@ namespace stream {
// Current Nvidia drivers have a bug where NVENC can deadlock the encoder thread with hardware-accelerated
// GPU scheduling enabled. If this happens, we will terminate ourselves and the service can restart.
// The alternative is that Sunshine can never start another session until it's manually restarted.
auto task = []() {
bool do_kill = false;
std::thread{[&do_kill]() {
Sleep(10000);
if(do_kill)
return;
BOOST_LOG(fatal) << "Hang detected! Session failed to terminate in 10 seconds."sv;
log_flush();
std::abort();
};
auto force_kill = task_pool.pushDelayed(task, 10s).task_id;
auto fg = util::fail_guard([&force_kill]() {
// Cancel the kill task if we manage to return from this function
task_pool.cancel(force_kill);
}};
auto fg = util::fail_guard([&do_kill]() {
do_kill = true;
});
BOOST_LOG(debug) << "Waiting for video to end..."sv;

View File

@ -1,12 +0,0 @@
/**
* @file src/version.h.in
* @brief Version definitions for Sunshine.
* @note The final `version.h` is generated from this file during the CMake build.
*/
#pragma once
#define PROJECT_NAME "@PROJECT_NAME@"
#define PROJECT_VER "@PROJECT_VERSION@"
#define PROJECT_VER_MAJOR "@PROJECT_VERSION_MAJOR@"
#define PROJECT_VER_MINOR "@PROJECT_VERSION_MINOR@"
#define PROJECT_VER_PATCH "@PROJECT_VERSION_PATCH@"