diff --git a/assets/sunshine.conf b/assets/sunshine.conf index 3831ff73..daaf000b 100644 --- a/assets/sunshine.conf +++ b/assets/sunshine.conf @@ -117,3 +117,14 @@ # See x264 --fullhelp for the different presets # preset = superfast # tune = zerolatency +# +# +############################################## +# Some configurable parameters, are merely toggles for specific features +# The first occurrence turns it on, the second occurence turns it off, the third occurence turns it on again, etc, etc +# Here, you change the default state of any flag +# +# To set the initial state of flags -0 and -1 to on, set the following flags: +# flags = 01 +# +# See: sunshine --help for all options under the header: flags diff --git a/sunshine/config.cpp b/sunshine/config.cpp index 81ab710e..0dce4186 100644 --- a/sunshine/config.cpp +++ b/sunshine/config.cpp @@ -53,7 +53,8 @@ input_t input { }; sunshine_t sunshine { - 2 // min_log_level + 2, // min_log_level + 0 // flags }; bool whitespace(char ch) { @@ -74,7 +75,7 @@ std::optional> parse_line(std::string_view:: return std::nullopt; } - auto end_name = std::find_if(std::make_reverse_iterator(eq - 1), std::make_reverse_iterator(begin), std::not_fn(whitespace)).base(); + auto end_name = std::find_if(std::make_reverse_iterator(eq), std::make_reverse_iterator(begin), std::not_fn(whitespace)).base(); auto begin_val = std::find_if(eq + 1, end, std::not_fn(whitespace)); return std::pair { to_string(begin, end_name), to_string(begin_val, end) }; @@ -148,15 +149,38 @@ void int_between_f(std::unordered_map &vars, const std } } -void parse_file(const char *file) { - std::ifstream in(file); +void print_help(const char *name) { + std::cout << + "Usage: "sv << name << " [options] [/path/to/configuration_file]"sv << std::endl << + " Any configurable option can be overwritten with: ="sv << std::endl << std::endl << + " --help | print help"sv << std::endl << std::endl << + " flags"sv << std::endl << + " -0 | Read PIN from stdin"sv << std::endl << + " -1 | Do not read/write state to/from disk" << std::endl; +} - auto vars = parse_config(std::string { - // Quick and dirty - std::istreambuf_iterator(in), - std::istreambuf_iterator() - }); +int apply_flags(const char *line) { + int ret = 0; + while(*line != '\0') { + switch(*line) { + case '0': + config::sunshine.flags[config::flag::PIN_STDIN].flip(); + break; + case '1': + config::sunshine.flags[config::flag::CLEAN_SLATE].flip(); + break; + default: + std::cout << "Warning: Unrecognized flag: ["sv << *line << ']' << std::endl; + ret = -1; + } + ++line; + } + + return ret; +} + +void apply_config(std::unordered_map &&vars) { for(auto &[name, val] : vars) { std::cout << "["sv << name << "] -- ["sv << val << ']' << std::endl; } @@ -191,7 +215,7 @@ void parse_file(const char *file) { if(to != -1) { stream.ping_timeout = std::chrono::milliseconds(to); } - + int_between_f(vars, "channels", stream.channels, { 1, std::numeric_limits::max() }); @@ -237,6 +261,13 @@ void parse_file(const char *file) { } } + auto it = vars.find("flags"s); + if(it != std::end(vars)) { + apply_flags(it->second.c_str()); + + vars.erase(it); + } + if(sunshine.min_log_level <= 3) { for(auto &[var,_] : vars) { std::cout << "Warning: Unrecognized configurable option ["sv << var << ']' << std::endl; @@ -244,5 +275,63 @@ void parse_file(const char *file) { } } +int parse(int argc, char *argv[]) { + const char *config_file = SUNSHINE_ASSETS_DIR "/sunshine.conf"; + std::unordered_map cmd_vars; + + for(auto x = argc -1; x > 0; --x) { + auto line = argv[x]; + + if(line == "--help"sv) { + print_help(*argv); + return 1; + } + else if(*line == '-') { + 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) { + config_file = line; + } + else { + auto var = parse_line(line, line_end); + if(!var) { + print_help(*argv); + return -1; + } + + cmd_vars.emplace(std::move(*var)); + } + } + } + + std::ifstream in { config_file }; + + if(!in.is_open()) { + std::cout << "Error: Couldn't open "sv << config_file << std::endl; + + return -1; + } + + auto vars = parse_config(std::string { + // Quick and dirty + std::istreambuf_iterator(in), + std::istreambuf_iterator() + }); + + for(auto &var : cmd_vars) { + vars.emplace(std::move(var)); + } + + apply_config(std::move(vars)); + + return 0; +} } diff --git a/sunshine/config.h b/sunshine/config.h index 9185b9ef..6a932146 100644 --- a/sunshine/config.h +++ b/sunshine/config.h @@ -3,6 +3,7 @@ #include #include +#include namespace config { struct video_t { @@ -54,8 +55,18 @@ struct input_t { std::chrono::milliseconds back_button_timeout; }; +namespace flag { +enum flag_e : std::size_t { + PIN_STDIN = 0, // Read PIN from stdin instead of http + CLEAN_SLATE, // Do not load or save state + FLAG_SIZE +}; +} + struct sunshine_t { int min_log_level; + + std::bitset flags; }; extern video_t video; @@ -65,7 +76,7 @@ extern nvhttp_t nvhttp; extern input_t input; extern sunshine_t sunshine; -void parse_file(const char *file); +int parse(int argc, char *argv[]); } #endif diff --git a/sunshine/main.cpp b/sunshine/main.cpp index 1d58b88c..1405008a 100644 --- a/sunshine/main.cpp +++ b/sunshine/main.cpp @@ -64,16 +64,8 @@ void on_signal(int sig, FN &&fn) { } int main(int argc, char *argv[]) { - const char *config_file = SUNSHINE_ASSETS_DIR "/sunshine.conf"; - if(argc > 1) { - config_file = argv[1]; - } - - if(!std::filesystem::exists(config_file)) { - std::cout << "Warning: Couldn't find configuration file ["sv << config_file << ']' << std::endl; - } - else { - config::parse_file(config_file); + if(config::parse(argc, argv)) { + return 0; } sink = boost::make_shared(); diff --git a/sunshine/nvhttp.cpp b/sunshine/nvhttp.cpp index 4d74fd78..a1583f20 100644 --- a/sunshine/nvhttp.cpp +++ b/sunshine/nvhttp.cpp @@ -162,7 +162,9 @@ void update_id_client(const std::string &uniqueID, std::string &&cert, op_e op) break; } - save_state(); + if(!config::sunshine.flags[config::flag::CLEAN_SLATE]) { + save_state(); + } } void getservercert(pair_session_t &sess, pt::ptree &tree, const std::string &pin) { @@ -349,8 +351,20 @@ void pair(std::shared_ptr> &add_cert, std::shared_ auto ptr = map_id_sess.emplace(sess.client.uniqueID, std::move(sess)).first; ptr->second.async_insert_pin.salt = std::move(args.at("salt"s)); - ptr->second.async_insert_pin.response = std::move(response); - return; + + if(config::sunshine.flags[config::flag::PIN_STDIN]) { + std::string pin; + + std::cout << "Please insert pin: "sv; + std::getline(std::cin, pin); + + getservercert(ptr->second, tree, pin); + } + else { + ptr->second.async_insert_pin.response = std::move(response); + + return; + } } else if(it->second == "pairchallenge"sv) { tree.put("root.paired", 1); @@ -710,6 +724,17 @@ int create_creds(const std::string &pkey, const std::string &cert) { } void start(std::shared_ptr shutdown_event) { + bool clean_slate = config::sunshine.flags[config::flag::CLEAN_SLATE]; + if(clean_slate) { + unique_id = util::uuid_t::generate().string(); + + auto dir = std::filesystem::temp_directory_path() / "Sushine"sv; + + config::nvhttp.cert = dir / ("cert-"s + unique_id); + config::nvhttp.pkey = dir / ("pkey-"s + unique_id); + } + + if(!fs::exists(config::nvhttp.pkey) || !fs::exists(config::nvhttp.cert)) { if(create_creds(config::nvhttp.pkey, config::nvhttp.cert)) { shutdown_event->raise(true); @@ -718,7 +743,10 @@ void start(std::shared_ptr shutdown_event) { } origin_pin_allowed = net::from_enum_string(config::nvhttp.origin_pin_allowed); - load_state(); + + if(!clean_slate) { + load_state(); + } conf_intern.pkey = read_file(config::nvhttp.pkey.c_str()); conf_intern.servercert = read_file(config::nvhttp.cert.c_str());