Groundwork for running different applications
This commit is contained in:
@@ -69,12 +69,15 @@ set(SUNSHINE_TARGET_FILES
|
|||||||
sunshine/stream.cpp
|
sunshine/stream.cpp
|
||||||
sunshine/stream.h
|
sunshine/stream.h
|
||||||
sunshine/video.cpp
|
sunshine/video.cpp
|
||||||
sunshine/video.h sunshine/thread_safe.h
|
sunshine/video.h
|
||||||
|
sunshine/thread_safe.h
|
||||||
sunshine/input.cpp
|
sunshine/input.cpp
|
||||||
sunshine/input.h
|
sunshine/input.h
|
||||||
sunshine/audio.cpp
|
sunshine/audio.cpp
|
||||||
sunshine/audio.h
|
sunshine/audio.h
|
||||||
sunshine/platform/common.h
|
sunshine/platform/common.h
|
||||||
|
sunshine/process.cpp
|
||||||
|
sunshine/process.h
|
||||||
${PLATFORM_TARGET_FILES})
|
${PLATFORM_TARGET_FILES})
|
||||||
|
|
||||||
include_directories(
|
include_directories(
|
||||||
|
|||||||
@@ -14,12 +14,36 @@ extern "C" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "platform/common.h"
|
|
||||||
|
#include "process.h"
|
||||||
|
|
||||||
using namespace std::literals;
|
using namespace std::literals;
|
||||||
int main(int argc, char *argv[]) {
|
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(argc > 1) {
|
||||||
if(!std::filesystem::exists(argv[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;
|
return 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ struct gamepad_state_t {
|
|||||||
|
|
||||||
std::string get_local_ip();
|
std::string get_local_ip();
|
||||||
|
|
||||||
|
void interrupt_process(std::uint64_t handle);
|
||||||
|
|
||||||
mic_t microphone();
|
mic_t microphone();
|
||||||
audio_t audio(mic_t &mic, std::uint32_t sample_size);
|
audio_t audio(mic_t &mic, std::uint32_t sample_size);
|
||||||
|
|
||||||
|
|||||||
@@ -84,6 +84,10 @@ std::string get_local_ip(int family) {
|
|||||||
|
|
||||||
std::string get_local_ip() { return get_local_ip(AF_INET); }
|
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 {
|
struct display_attr_t {
|
||||||
display_attr_t() : display { XOpenDisplay(nullptr) }, window { DefaultRootWindow(display) }, attr {} {
|
display_attr_t() : display { XOpenDisplay(nullptr) }, window { DefaultRootWindow(display) }, attr {} {
|
||||||
refresh();
|
refresh();
|
||||||
|
|||||||
143
sunshine/process.cpp
Normal file
143
sunshine/process.cpp
Normal 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
67
sunshine/process.h
Normal 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
|
||||||
@@ -14,12 +14,7 @@
|
|||||||
namespace safe {
|
namespace safe {
|
||||||
template<class T>
|
template<class T>
|
||||||
class event_t {
|
class event_t {
|
||||||
using status_t = util::either_t<
|
using status_t = util::optional_t<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>>;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
template<class...Args>
|
template<class...Args>
|
||||||
@@ -82,12 +77,7 @@ private:
|
|||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
class queue_t {
|
class queue_t {
|
||||||
using status_t = util::either_t<
|
using status_t = util::optional_t<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>>;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
template<class ...Args>
|
template<class ...Args>
|
||||||
|
|||||||
@@ -93,6 +93,14 @@ struct __false_v<T, std::enable_if_t<std::is_same_v<T, bool>>> {
|
|||||||
template<class T>
|
template<class T>
|
||||||
static constexpr auto false_v = __false_v<T>::value;
|
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>
|
template<class T>
|
||||||
class FailGuard {
|
class FailGuard {
|
||||||
public:
|
public:
|
||||||
|
|||||||
Reference in New Issue
Block a user