Execute commands defined in apps.json

This commit is contained in:
loki 2019-12-15 19:36:22 +01:00
parent 20f5f90588
commit b7eab0883c
9 changed files with 194 additions and 105 deletions

View File

@ -1,29 +1,34 @@
{
"env":[
{ "VAR":"VAL", "VAR2":"VAL2" }
{
"DISPLAY":":0",
"DRI_PRIME":"1",
"XAUTHORITY":"/home/loki/.Xauthority"
}
],
"apps":[
{
"name":"echo-1",
"output":"output.txt",
"cmd":"sh -c \"echo $VAR\"",
"name":"Low Res Desktop 2",
"cmd":"sh -c \"while true; do sleep 10000; done;\"",
"prep-cmd":[
{ "do":"echo pre-1", "undo":"echo post-1" },
{ "do":"echo pre-2" },
{ "do":"echo pre-3", "undo":"echo post-3" }
{ "do":"xrandr --output HDMI-1 --mode 1920x1080", "undo":"xrandr --output HDMI-1 --mode 1920x1200" }
]
},
{
"name":"echo-2",
"output":"output.txt",
"cmd":"sleep 10",
"name":"Low Res Desktop",
"cmd":"sh -c \"while true; do sleep 10000; done;\"",
"prep-cmd":[
{ "do":"echo pre-1", "undo":"echo post-1" },
{ "do":"echo pre-2" },
{ "do":"echo pre-3", "undo":"echo post-3" }
{ "do":"xrandr --output HDMI-1 --mode 1920x1080", "undo":"xrandr --output HDMI-1 --mode 1920x1200" }
]
},
{
"name":"Steam BigPicture",
"output":"steam.txt",
"cmd":"steam -bigpicture",
"prep-cmd":[
{ "do":"sh -c \"PATH=$PATH:$HOME/.local/bin sudo freeHugePages\""},
{ "do":"sh -c \"PATH=$PATH:$HOME/.local/bin sudo claim_amdgpu\"" }]
}
]
}

View File

@ -6,9 +6,6 @@
#include <filesystem>
#include <iostream>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include "nvhttp.h"
#include "stream.h"
#include "config.h"
@ -18,85 +15,9 @@ extern "C" {
#include <rs.h>
}
using namespace std::literals;
namespace pt = boost::property_tree;
std::optional<proc::proc_t> parse(const std::string& file_name) {
pt::ptree tree;
try {
pt::read_json(file_name, tree);
auto &apps_node = tree.get_child("apps"s);
auto &env_vars = tree.get_child("env"s);
boost::process::environment env = boost::this_process::environment();
std::unordered_map<std::string, proc::ctx_t> apps;
for(auto &[_,app_node] : apps_node) {
proc::ctx_t ctx;
auto &prep_nodes = app_node.get_child("prep-cmd"s);
auto name = app_node.get<std::string>("name"s);
auto output = app_node.get_optional<std::string>("output"s);
auto cmd = app_node.get<std::string>("cmd"s);
std::vector<proc::cmd_t> prep_cmds;
prep_cmds.reserve(prep_nodes.size());
for(auto &[_, prep_node] : prep_nodes) {
auto do_cmd = prep_node.get<std::string>("do"s);
auto undo_cmd = prep_node.get_optional<std::string>("undo"s);
if(undo_cmd) {
prep_cmds.emplace_back(std::move(do_cmd), std::move(*undo_cmd));
}
else {
prep_cmds.emplace_back(std::move(do_cmd));
}
}
if(output) {
ctx.output = std::move(*output);
}
ctx.cmd = std::move(cmd);
ctx.prep_cmds = std::move(prep_cmds);
apps.emplace(std::move(name), std::move(ctx));
}
for(auto &[_,env_var] : env_vars) {
for(auto &[name,val] : env_var) {
env[name] = val.get_value<std::string>();
}
}
return proc::proc_t {
std::move(env), std::move(apps)
};
} catch (std::exception &e) {
std::cout << e.what() << std::endl;
}
return std::nullopt;
}
int main(int argc, char *argv[]) {
auto proc_opt = parse(SUNSHINE_ASSETS_DIR "/apps.json");
if(!proc_opt) {
return 7;
}
auto &proc = *proc_opt;
proc.execute("echo-2");
std::this_thread::sleep_for(50ms);
proc.execute("echo-1");
std::this_thread::sleep_for(50ms);
return proc.running();
if(argc > 1) {
if(!std::filesystem::exists(argv[1])) {
std::cout << "Error: Couldn't find configuration file ["sv << argv[1] << ']' << std::endl;
@ -106,6 +27,12 @@ int main(int argc, char *argv[]) {
config::parse_file(argv[1]);
}
auto proc_opt = proc::parse(SUNSHINE_ASSETS_DIR "/apps.json");
if(!proc_opt) {
return 7;
}
proc::proc = std::move(*proc_opt);
reed_solomon_init();
std::thread httpThread { nvhttp::start };

View File

@ -20,6 +20,7 @@
#include "stream.h"
#include "nvhttp.h"
#include "platform/common.h"
#include "process.h"
namespace nvhttp {
@ -441,20 +442,26 @@ void applist(std::shared_ptr<typename SimpleWeb::ServerBase<T>::Response> respon
}
auto &apps = tree.add_child("root", pt::ptree {});
pt::ptree desktop;
pt::ptree fakegame;
apps.put("<xmlattr>.status_code", 200);
desktop.put("IsHdrSupported"s, 0);
desktop.put("AppTitle"s, "Desktop");
desktop.put("ID"s, 1);
fakegame.put("IsHdrSupported"s, 0);
fakegame.put("AppTitle"s, "FakeGame");
fakegame.put("ID"s, 2);
int x = 2;
for(auto &[name, proc] : proc::proc.get_apps()) {
pt::ptree app;
app.put("IsHdrSupported"s, 0);
app.put("AppTitle"s, name);
app.put("ID"s, x++);
apps.push_back(std::make_pair("App", std::move(app)));
}
apps.push_back(std::make_pair("App", desktop));
apps.push_back(std::make_pair("App", fakegame));
}
template<class T>
@ -462,6 +469,17 @@ void launch(std::shared_ptr<typename SimpleWeb::ServerBase<T>::Response> respons
print_req<T>(request);
auto args = request->parse_query_string();
auto appid = util::from_view(args.at("appid")) -2;
stream::app_name.clear();
if(appid >= 0) {
auto pos = std::begin(proc::proc.get_apps());
std::advance(pos, appid);
stream::app_name = pos->first;
}
auto clientID = args.at("uniqueid"s);
auto aesKey = *util::from_hex<crypto::aes_t>(args.at("rikey"s), true);
uint32_t prepend_iv = util::endian::big<uint32_t>(util::from_view(args.at("rikeyid"s)));

View File

@ -34,7 +34,7 @@ struct gamepad_state_t {
std::string get_local_ip();
void interrupt_process(std::uint64_t handle);
void terminate_process(std::uint64_t handle);
mic_t microphone();
audio_t audio(mic_t &mic, std::uint32_t sample_size);

View File

@ -84,8 +84,8 @@ std::string get_local_ip(int family) {
std::string get_local_ip() { return get_local_ip(AF_INET); }
void interrupt_process(std::uint64_t handle) {
kill((pid_t)handle, SIGINT);
void terminate_process(std::uint64_t handle) {
kill((pid_t)handle, SIGTERM);
}
struct display_attr_t {

View File

@ -6,6 +6,9 @@
#include <string>
#include <iostream>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include "process.h"
#include "config.h"
#include "utility.h"
@ -14,6 +17,9 @@
namespace proc {
using namespace std::literals;
namespace bp = boost::process;
namespace pt = boost::property_tree;
proc_t proc;
template<class Rep, class Period>
void process_end(bp::child &proc, const std::chrono::duration<Rep, Period>& rel_time) {
@ -21,10 +27,12 @@ void process_end(bp::child &proc, const std::chrono::duration<Rep, Period>& rel_
return;
}
platf::interrupt_process((std::uint64_t)proc.native_handle());
std::cout << "Interruping Child-Process"sv << std::endl;
platf::terminate_process((std::uint64_t) proc.native_handle());
// Force termination if it takes too long
if(!proc.wait_for(rel_time)) {
std::cout << "Force termination Child-Process"sv << std::endl;
proc.terminate();
}
}
@ -40,8 +48,10 @@ int exe(const std::string &cmd, bp::environment &env, file_t &file, std::error_c
int proc_t::execute(const std::string &name) {
auto it = _name_to_proc.find(name);
std::cout << "Ensure clean slate"sv << std::endl;
// Ensure starting from a clean slate
_undo_pre_cmd();
std::cout << "Clean slate"sv << std::endl;
if(it == std::end(_name_to_proc)) {
std::cout << "Error: Couldn't find ["sv << name << ']' << std::endl;
@ -137,7 +147,79 @@ void proc_t::_undo_pre_cmd() {
_pipe.reset();
}
const std::unordered_map<std::string, ctx_t> &proc_t::get_apps() const {
return _name_to_proc;
}
proc_t::~proc_t() {
_undo_pre_cmd();
}
std::optional<proc::proc_t> parse(const std::string& file_name) {
pt::ptree tree;
try {
pt::read_json(file_name, tree);
auto &apps_node = tree.get_child("apps"s);
auto &env_vars = tree.get_child("env"s);
boost::process::environment env = boost::this_process::environment();
std::unordered_map<std::string, proc::ctx_t> apps;
for(auto &[_,app_node] : apps_node) {
proc::ctx_t ctx;
auto &prep_nodes = app_node.get_child("prep-cmd"s);
auto name = app_node.get<std::string>("name"s);
auto output = app_node.get_optional<std::string>("output"s);
auto cmd = app_node.get<std::string>("cmd"s);
std::vector<proc::cmd_t> prep_cmds;
prep_cmds.reserve(prep_nodes.size());
for(auto &[_, prep_node] : prep_nodes) {
auto do_cmd = prep_node.get<std::string>("do"s);
auto undo_cmd = prep_node.get_optional<std::string>("undo"s);
if(undo_cmd) {
prep_cmds.emplace_back(std::move(do_cmd), std::move(*undo_cmd));
}
else {
prep_cmds.emplace_back(std::move(do_cmd));
}
}
if(output) {
ctx.output = std::move(*output);
}
ctx.cmd = std::move(cmd);
ctx.prep_cmds = std::move(prep_cmds);
apps.emplace(std::move(name), std::move(ctx));
}
for(auto &[_,env_var] : env_vars) {
for(auto &[name,val] : env_var) {
env[name] = val.get_value<std::string>();
}
}
return proc::proc_t {
std::move(env), std::move(apps)
};
} catch (std::exception &e) {
std::cout << e.what() << std::endl;
}
return std::nullopt;
}
void refresh(const std::string &file_name) {
auto proc_opt = proc::parse(file_name);
if(proc_opt) {
proc = std::move(*proc_opt);
}
}
}

View File

@ -6,8 +6,10 @@
#define SUNSHINE_PROCESS_H
#include <unordered_map>
#include <optional>
#include <boost/process.hpp>
#include "utility.h"
namespace proc {
@ -41,7 +43,7 @@ struct ctx_t {
class proc_t {
public:
KITTY_DEFAULT_CONSTR(proc_t)
KITTY_DEFAULT_CONSTR_THROW(proc_t)
proc_t(
boost::process::environment &&env,
@ -54,6 +56,7 @@ public:
~proc_t();
const std::unordered_map<std::string, ctx_t> &get_apps() const;
private:
void _undo_pre_cmd();
@ -66,5 +69,9 @@ private:
std::vector<cmd_t>::const_iterator _undo_begin;
};
void refresh(const std::string &file_name);
std::optional<proc::proc_t> parse(const std::string& file_name);
extern proc_t proc;
}
#endif //SUNSHINE_PROCESS_H

View File

@ -31,6 +31,7 @@ extern "C" {
#include "thread_safe.h"
#include "crypto.h"
#include "input.h"
#include "process.h"
#define IDX_START_A 0
#define IDX_REQUEST_IDR_FRAME 0
@ -88,8 +89,10 @@ struct audio_packet_raw_t {
#pragma pack(pop)
//TODO: Bundle in thread safe container
crypto::aes_t gcm_key;
crypto::aes_t iv;
std::string app_name;
struct config_t {
audio::config_t audio;
@ -114,6 +117,8 @@ struct session_t {
crypto::aes_t gcm_key;
crypto::aes_t iv;
std::string app_name;
} session;
void free_msg(PRTSP_MESSAGE msg) {
@ -166,7 +171,7 @@ public:
parseRtspMessage(req.get(), (char*)packet->data, packet->dataLength);
for(auto option = req->options; option != nullptr; option = option->next) {
if("Content-length"sv == option->option) {
_queue_packet = std::make_pair(std::move(peer), std::move(packet));
_queue_packet = std::make_pair(peer, std::move(packet));
return;
}
}
@ -272,6 +277,7 @@ public:
}
}
void map(uint16_t type, std::function<void(const std::string_view&)> cb);
void send(const std::string_view &payload);
private:
std::unordered_map<std::uint16_t, std::function<void(const std::string_view&)>> _map_type_cb;
ENetAddress _addr;
@ -431,6 +437,17 @@ void control_server_t::map(uint16_t type, std::function<void(const std::string_v
_map_type_cb.emplace(type, std::move(cb));
}
void control_server_t::send(const std::string_view & payload) {
std::for_each(_host->peers, _host->peers + _host->peerCount, [payload](auto &peer) {
auto packet = enet_packet_create(payload.data(), payload.size(), ENET_PACKET_FLAG_RELIABLE);
if(enet_peer_send(&peer, 0, packet)) {
enet_packet_destroy(packet);
}
});
enet_host_flush(_host.get());
}
void controlThread(video::idr_event_t idr_events) {
control_server_t server { CONTROL_PORT };
@ -515,6 +532,21 @@ void controlThread(video::idr_event_t idr_events) {
session.audio_packets->stop();
}
if(!session.app_name.empty() && !proc::proc.running()) {
std::cout << "Process terminated"sv << std::endl;
std::uint16_t reason = 0x0100;
std::array<std::uint16_t, 2> payload;
payload[0] = packetTypes[IDX_TERMINATION];
payload[1] = reason;
server.send(std::string_view {(char*)payload.data(), payload.size()});
session.video_packets->stop();
session.audio_packets->stop();
}
server.iterate(500ms);
}
}
@ -885,6 +917,22 @@ void cmd_announce(host_t &host, peer_t peer, msg_t &&req) {
return;
}
if(!app_name.empty() && session.app_name != app_name ) {
if(auto err_code = proc::proc.execute(app_name)) {
if(err_code == 404) {
respond(host, peer, &option, 404, (app_name + " NOT FOUND").c_str(), req->sequenceNumber, {});
return;
}
else {
respond(host, peer, &option, 500, "INTERNAL ERROR", req->sequenceNumber, {});
return;
}
}
}
session.app_name = std::move(app_name);
std::copy(std::begin(gcm_key), std::end(gcm_key), std::begin(session.gcm_key));
std::copy(std::begin(iv), std::end(iv), std::begin(session.iv));

View File

@ -9,8 +9,10 @@
namespace stream {
//FIXME: Make thread safe
extern crypto::aes_t gcm_key;
extern crypto::aes_t iv;
extern std::string app_name;
void rtpThread();