Groundwork for running different applications

This commit is contained in:
loki 2019-12-14 23:57:04 +01:00
parent 5541f9dca8
commit d17f37db12
8 changed files with 256 additions and 15 deletions

View File

@ -69,12 +69,15 @@ set(SUNSHINE_TARGET_FILES
sunshine/stream.cpp
sunshine/stream.h
sunshine/video.cpp
sunshine/video.h sunshine/thread_safe.h
sunshine/video.h
sunshine/thread_safe.h
sunshine/input.cpp
sunshine/input.h
sunshine/audio.cpp
sunshine/audio.h
sunshine/platform/common.h
sunshine/process.cpp
sunshine/process.h
${PLATFORM_TARGET_FILES})
include_directories(

View File

@ -14,12 +14,36 @@ extern "C" {
}
#include "config.h"
#include "platform/common.h"
#include "process.h"
using namespace std::literals;
int main(int argc, char *argv[]) {
std::vector<proc::cmd_t> pre_cmds {
{ "echo pre-1", "echo post-1" },
{ "echo pre-2", "" },
{ "echo pre-3", "echo post-3" }
};
std::unordered_map<std::string, proc::ctx_t> map {
{ "echo", { std::move(pre_cmds), R"(echo \"middle\")", "output.txt" } }
};
boost::process::environment env = boost::this_process::environment();
proc::proc_t proc(std::move(env), std::move(map));
proc.execute("echo"s);
std::this_thread::sleep_for(50ms);
proc.execute("echo"s);
std::this_thread::sleep_for(50ms);
return proc.running();
if(argc > 1) {
if(!std::filesystem::exists(argv[1])) {
std::cout << "Error: Couln't find configuration file ["sv << argv[1] << ']' << std::endl;
std::cout << "Error: Couldn't find configuration file ["sv << argv[1] << ']' << std::endl;
return 7;
}

View File

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

View File

@ -84,6 +84,10 @@ 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, SIGTERM);
}
struct display_attr_t {
display_attr_t() : display { XOpenDisplay(nullptr) }, window { DefaultRootWindow(display) }, attr {} {
refresh();

143
sunshine/process.cpp Normal file
View File

@ -0,0 +1,143 @@
//
// Created by loki on 12/14/19.
//
#include <vector>
#include <string>
#include <iostream>
#include "process.h"
#include "config.h"
#include "utility.h"
#include "platform/common.h"
namespace proc {
using namespace std::literals;
namespace bp = boost::process;
template<class Rep, class Period>
void process_end(bp::child &proc, const std::chrono::duration<Rep, Period>& rel_time) {
if(!proc.running()) {
return;
}
platf::interrupt_process((std::uint64_t)proc.native_handle());
// Force termination if it takes too long
if(!proc.wait_for(rel_time)) {
proc.terminate();
}
}
int exe(const std::string &cmd, bp::environment &env, file_t &file, std::error_code &ec) {
if(cmd.empty() || cmd == "null"sv) {
return bp::system(cmd, env, bp::std_out > bp::null, bp::std_err > bp::null, ec);
}
return bp::system(cmd, env, bp::std_out > file.get(), bp::std_err > file.get(), ec);
}
int proc_t::execute(const std::string &name) {
auto it = _name_to_proc.find(name);
// Ensure starting from a clean slate
_undo_pre_cmd();
if(it == std::end(_name_to_proc)) {
std::cout << "Error: Couldn't find ["sv << name << ']' << std::endl;
return 404;
}
auto &proc = it->second;
_undo_begin = std::begin(proc.pre_cmds);
_undo_it = _undo_begin;
if(!proc.cmd_output.empty() && proc.cmd_output != "null"sv) {
_pipe.reset(fopen(proc.cmd_output.c_str(), "a"));
}
std::error_code ec;
//Executed when returning from function
auto fg = util::fail_guard([&]() {
_undo_pre_cmd();
});
for(; _undo_it != std::end(proc.pre_cmds); ++_undo_it) {
auto &cmd = _undo_it->do_cmd;
std::cout << "Executing: ["sv << cmd << ']' << std::endl;
auto ret = exe(cmd, _env, _pipe, ec);
if(ec) {
std::cout << "Error: System: "sv << ec.message() << std::endl;
return -1;
}
if(ret != 0) {
std::cout << "Error: return code ["sv << ret << ']';
return -1;
}
}
std::cout << "Starting ["sv << proc.cmd << ']' << std::endl;
if(proc.cmd_output.empty() || proc.cmd_output == "null"sv) {
_process = bp::child(proc.cmd, _env, bp::std_out > bp::null, bp::std_err > bp::null, ec);
}
else {
_process = bp::child(proc.cmd, _env, bp::std_out > proc.cmd_output, bp::std_err > proc.cmd_output, ec);
}
if(ec) {
std::cout << "Error: System: "sv << ec.message() << std::endl;
return -1;
}
fg.disable();
return 0;
}
bool proc_t::running() {
return _process.running();
}
void proc_t::_undo_pre_cmd() {
std::error_code ec;
// Ensure child process is terminated
process_end(_process, 10s);
if(ec) {
std::cout << "FATAL Error: System: "sv << ec.message() << std::endl;
std::abort();
}
for(;_undo_it != _undo_begin; --_undo_it) {
auto &cmd = (_undo_it - 1)->undo_cmd;
if(cmd.empty()) {
continue;
}
std::cout << "Executing: ["sv << cmd << ']' << std::endl;
auto ret = exe(cmd, _env, _pipe, ec);
if(ec) {
std::cout << "FATAL Error: System: "sv << ec.message() << std::endl;
std::abort();
}
if(ret != 0) {
std::cout << "FATAL Error: return code ["sv << ret << ']';
std::abort();
}
}
_pipe.reset();
}
proc_t::~proc_t() {
_undo_pre_cmd();
}
}

67
sunshine/process.h Normal file
View File

@ -0,0 +1,67 @@
//
// Created by loki on 12/14/19.
//
#ifndef SUNSHINE_PROCESS_H
#define SUNSHINE_PROCESS_H
#include <unordered_map>
#include <boost/process.hpp>
#include "utility.h"
namespace proc {
using file_t = util::safe_ptr_v2<FILE, int, fclose>;
struct cmd_t {
std::string do_cmd;
// Executed when proc_t has finished running, meant to reverse 'do_cmd' if applicable
std::string undo_cmd;
};
/*
* pre_cmds -- guaranteed to be executed unless any of the commands fail.
* cmd -- Runs indefinitely until:
* No session is running and a different set of commands it to be executed
* Command exits
* cmd_output --
* empty -- The output of the commands are appended to the output of sunshine
* "null" -- The output of the commands are discarded
* filename -- The output of the commands are appended to filename
*/
struct ctx_t {
std::vector<cmd_t> pre_cmds;
std::string cmd;
std::string cmd_output;
};
class proc_t {
public:
KITTY_DEFAULT_CONSTR(proc_t)
proc_t(
boost::process::environment &&env,
std::unordered_map<std::string, ctx_t> &&name_to_proc) :
_env(std::move(env)),
_name_to_proc(std::move(name_to_proc)) {}
int execute(const std::string &name);
bool running();
~proc_t();
private:
void _undo_pre_cmd();
boost::process::environment _env;
std::unordered_map<std::string, ctx_t> _name_to_proc;
boost::process::child _process;
file_t _pipe;
std::vector<cmd_t>::const_iterator _undo_it;
std::vector<cmd_t>::const_iterator _undo_begin;
};
}
#endif //SUNSHINE_PROCESS_H

View File

@ -14,12 +14,7 @@
namespace safe {
template<class T>
class event_t {
using status_t = util::either_t<
(std::is_same_v<T, bool> ||
util::instantiation_of_v<std::unique_ptr, T> ||
util::instantiation_of_v<std::shared_ptr, T> ||
std::is_pointer_v<T>),
T, std::optional<T>>;
using status_t = util::optional_t<T>;
public:
template<class...Args>
@ -82,12 +77,7 @@ private:
template<class T>
class queue_t {
using status_t = util::either_t<
(std::is_same_v<T, bool> ||
util::instantiation_of_v<std::unique_ptr, T> ||
util::instantiation_of_v<std::shared_ptr, T> ||
std::is_pointer_v<T>),
T, std::optional<T>>;
using status_t = util::optional_t<T>;
public:
template<class ...Args>

View File

@ -93,6 +93,14 @@ struct __false_v<T, std::enable_if_t<std::is_same_v<T, bool>>> {
template<class T>
static constexpr auto false_v = __false_v<T>::value;
template<class T>
using optional_t = either_t<
(std::is_same_v<T, bool> ||
instantiation_of_v<std::unique_ptr, T> ||
instantiation_of_v<std::shared_ptr, T> ||
std::is_pointer_v<T>),
T, std::optional<T>>;
template<class T>
class FailGuard {
public: