1// SPDX-License-Identifier: GPL-3.0-or-later
2
3#include "LaunchContext.hpp"
4#include "mossh.hpp"
5
6#include <fstream>
7#include <iostream>
8#include <nlohmann/json.hpp>
9#include <thread>
10#include <unistd.h>
11
12using namespace std::string_view_literals;
13
14#define IsRequest(_type) (packet.type == rpc::_type::rpc_type + ".request"s)
15#define IsResponse(_type) (packet.type == rpc::_type::rpc_type + ".response"s)
16#define ResponseType(_type) (rpc::_type::rpc_type + ".response"s)
17
18namespace rpc
19{
20 struct Packet
21 {
22 std::string type;
23 nlohmann::json object;
24 NLOHMANN_DEFINE_TYPE_INTRUSIVE(Packet, type, object)
25 };
26
27 namespace RunCommand
28 {
29 constexpr static const auto rpc_type = "run-command"s;
30
31 struct RedirectionEntry
32 {
33 bool read = false, write = false, append = false;
34 std::string type; // "file" or "fd"
35 std::string target; // a file path or another fd
36 NLOHMANN_DEFINE_TYPE_INTRUSIVE(RedirectionEntry, type, target, read, write, append)
37 };
38
39 struct Request
40 {
41 std::string command;
42 std::vector<std::string> argv;
43 std::map<std::string, RedirectionEntry> redirections; // defect: should be int instead of string
44 NLOHMANN_DEFINE_TYPE_INTRUSIVE(Request, command, argv, redirections)
45 };
46
47 struct Response
48 {
49 int returncode;
50 NLOHMANN_DEFINE_TYPE_INTRUSIVE(Response, returncode)
51 };
52 } // namespace RunCommand
53
54 namespace Shutdown
55 {
56 constexpr static const auto rpc_type = "shutdown"s;
57
58 struct Request
59 {
60 std::string stub;
61 NLOHMANN_DEFINE_TYPE_INTRUSIVE(Request, stub)
62 };
63
64 struct Response
65 {
66 std::string stub;
67 NLOHMANN_DEFINE_TYPE_INTRUSIVE(Response, stub)
68 };
69 } // namespace Shutdown
70
71 namespace ReadFile
72 {
73 constexpr static const auto rpc_type = "read-file"s;
74
75 struct Request
76 {
77 std::string path;
78 NLOHMANN_DEFINE_TYPE_INTRUSIVE(Request, path)
79 };
80
81 struct Response
82 {
83 std::string content;
84 NLOHMANN_DEFINE_TYPE_INTRUSIVE(Response, content)
85 };
86 } // namespace ReadFile
87
88 namespace WriteFile
89 {
90 constexpr static const auto rpc_type = "write-file"s;
91
92 struct Request
93 {
94 std::string path;
95 std::string content;
96 NLOHMANN_DEFINE_TYPE_INTRUSIVE(Request, path, content)
97 };
98
99 struct Response
100 {
101 std::string stub;
102 NLOHMANN_DEFINE_TYPE_INTRUSIVE(Response, stub)
103 };
104 } // namespace WriteFile
105
106} // namespace rpc
107
108int do_jsonrpc()
109{
110 std::string input;
111
112 while (true)
113 {
114 std::getline(is&: std::cin, str&: input);
115 nlohmann::json j = nlohmann::json::parse(i&: input);
116
117 const auto packet = j.get<rpc::Packet>();
118
119 rpc::Packet resp;
120
121 if (IsRequest(RunCommand))
122 {
123 resp.type = ResponseType(RunCommand);
124 const auto request = packet.object.get<const rpc::RunCommand::Request>();
125
126 LaunchContext ctx{ request.argv };
127 ctx.should_wait = true;
128
129 for (const auto &[fd, redir] : request.redirections)
130 {
131 const auto fd_int = std::stoi(str: fd);
132 if (redir.type == "file")
133 ctx.redirect(fd: fd_int, redirection: std::make_unique<FileRedirection>(args: redir.target, args: BaseRedirection::IOMode::ReadWrite, args: redir.append));
134 else if (redir.type == "fd")
135 ctx.redirect(fd: fd_int, redirection: std::make_unique<FDRedirection>(args: std::stoi(str: redir.target), args: BaseRedirection::IOMode::ReadWrite));
136 else
137 std::cerr << "Skipped unknown redirection type: " << redir.type << std::endl;
138 }
139
140 const auto result = ctx.start();
141 if (!result)
142 std::cerr << "Failed to start program: " << ctx.command() << std::endl;
143
144 const rpc::RunCommand::Response response{ .returncode = ctx.exit_code };
145 resp.object = response;
146 }
147 else if (IsRequest(Shutdown))
148 {
149 resp.type = ResponseType(Shutdown);
150 const auto request = packet.object.get<const rpc::Shutdown::Request>();
151
152 // run shutdown in a separate thread
153 std::thread([]() { sleep(3), execute_line(in: "shutdown"); }).detach();
154
155 const rpc::Shutdown::Response response{
156 .stub = request.stub,
157 };
158
159 resp.object = response;
160 }
161 else if (IsRequest(ReadFile))
162 {
163 resp.type = ResponseType(ReadFile);
164 const auto request = packet.object.get<const rpc::ReadFile::Request>();
165
166 const auto read_file = [](const std::string &path) -> std::string
167 {
168 std::ifstream file(path);
169 if (!file.is_open())
170 return "";
171
172 std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
173 return content;
174 };
175
176 const auto content = read_file(request.path);
177 const rpc::ReadFile::Response response{ .content = content };
178
179 resp.object = response;
180 }
181 else if (IsRequest(WriteFile))
182 {
183 resp.type = ResponseType(WriteFile);
184 const auto request = packet.object.get<const rpc::WriteFile::Request>();
185
186 const auto write_file = [](const std::string &path, const std::string &content)
187 {
188 std::ofstream file(path);
189 if (!file.is_open())
190 return;
191
192 file << content;
193 };
194
195 write_file(request.path, request.content);
196 const rpc::WriteFile::Response response{ .stub = "" };
197
198 resp.object = response;
199 }
200 else
201 {
202 std::cerr << "Unknown packet type: " << packet.type << std::endl;
203 }
204
205 std::cout << std::endl << static_cast<nlohmann::json>(resp).dump() << std::endl;
206 }
207
208 return 0;
209}
210