imaginite / main.cpp
diamond-in's picture
Update main.cpp
1e68309 verified
#include <iostream>
#include <vector>
#include <string>
#include <cstring>
#include <sstream>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/poll.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <csignal>
#define PORT 7860
#define MAX_HISTORY 5 * 1024 * 1024
std::vector<char> output_history;
int master_read_fd = -1;
int master_write_fd = -1;
const char* HTML_PAGE = R"(
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Alpine RISC-V</title>
<style>
body { background-color: #000; color: #0f0; font-family: 'Courier New', monospace; height: 100vh; margin: 0; display: flex; flex-direction: column; overflow: hidden; }
#term { flex-grow: 1; padding: 15px; overflow-y: auto; white-space: pre-wrap; font-size: 14px; scroll-behavior: smooth; }
#input-wrapper { display: flex; border-top: 1px solid #333; padding: 10px; background: #111; }
#prompt { color: #0f0; padding-right: 10px; font-weight: bold; }
#cmd { background: transparent; border: none; color: white; flex-grow: 1; outline: none; font-family: inherit; font-size: 16px; }
.local-echo { color: #888; }
</style>
</head>
<body>
<div id="term">--- CLICK HERE TO ENABLE TYPING ---</div>
<div id="input-wrapper"><span id="prompt"># </span><input type="text" id="cmd" autocomplete="off"></div>
<script>
const term = document.getElementById('term');
const cmd = document.getElementById('cmd');
let offset = 0;
function poll() {
fetch('/poll?offset=' + offset)
.then(r => r.ok ? r.text() : '')
.then(txt => {
if(txt.length > 0) {
offset += txt.length;
term.innerText += txt;
term.scrollTop = term.scrollHeight;
}
})
.catch(e => console.log('Poll:', e))
.finally(() => setTimeout(poll, 300));
}
async function sendCommand(val) {
term.innerHTML += `<span class="local-echo">${val}</span>`;
term.scrollTop = term.scrollHeight;
await fetch('/input', { method: 'POST', body: val + '\n' });
}
cmd.addEventListener('keydown', (e) => {
if(e.key === 'Enter') {
const val = cmd.value;
cmd.value = '';
sendCommand(val);
}
});
document.addEventListener('click', () => cmd.focus());
try { cmd.focus(); } catch(e){}
poll();
</script>
</body>
</html>
)";
bool file_exists(const char* name) {
struct stat buffer;
return (stat(name, &buffer) == 0);
}
void start_vm() {
if (!file_exists("vmlinuz") || !file_exists("initramfs")) {
std::string err = "CRITICAL ERROR: Boot files missing. Check Dockerfile.\n";
output_history.insert(output_history.end(), err.begin(), err.end());
return;
}
int pipe_in[2], pipe_out[2];
if (pipe(pipe_in) < 0 || pipe(pipe_out) < 0) return;
if (fork() == 0) {
// CHILD
dup2(pipe_in[0], STDIN_FILENO);
dup2(pipe_out[1], STDOUT_FILENO);
dup2(pipe_out[1], STDERR_FILENO);
close(pipe_in[1]); close(pipe_out[0]);
const char* bios = file_exists("bios.elf") ? "bios.elf" : "default";
// --- UPDATED BOOT LOGIC ---
execlp("qemu-system-riscv64", "qemu-system-riscv64",
"-nographic",
"-machine", "virt",
"-m", "512M",
"-smp", "2",
"-bios", bios,
"-kernel", "vmlinuz",
"-initrd", "initramfs",
// NEW DRIVE CONFIG FOR ALPINE V3.23
"-device", "virtio-scsi-device,id=scsi0",
"-drive", "file=alpine.iso,format=raw,if=none,id=drive0",
"-device", "scsi-cd,bus=scsi0.0,drive=drive0",
"-serial", "stdio",
"-monitor", "none",
// Boot from CDROM (sr0)
"-append", "console=ttyS0 root=/dev/sr0 modules=sd-mod,usb-storage,sr_mod quiet",
NULL
);
exit(1);
}
// PARENT
close(pipe_in[0]); close(pipe_out[1]);
master_write_fd = pipe_in[1];
master_read_fd = pipe_out[0];
fcntl(master_read_fd, F_SETFL, O_NONBLOCK);
std::string msg = "--- SYSTEM ONLINE. LOADING CD-ROM... ---\n";
output_history.insert(output_history.end(), msg.begin(), msg.end());
}
void send_http(int client, std::string content, std::string type) {
std::string response =
"HTTP/1.1 200 OK\r\n"
"Content-Type: " + type + "\r\n"
"Content-Length: " + std::to_string(content.length()) + "\r\n"
"Connection: close\r\n"
"\r\n" +
content;
send(client, response.c_str(), response.length(), 0);
}
int main() {
signal(SIGPIPE, SIG_IGN);
int server = socket(AF_INET, SOCK_STREAM, 0);
int opt=1; setsockopt(server, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
struct sockaddr_in addr; addr.sin_family=AF_INET; addr.sin_addr.s_addr=INADDR_ANY; addr.sin_port=htons(PORT);
bind(server, (struct sockaddr*)&addr, sizeof(addr));
listen(server, 10);
std::cout << "SERVER LISTENING ON 7860" << std::endl;
start_vm();
struct pollfd p[2];
p[0].fd = server; p[0].events = POLLIN;
p[1].fd = master_read_fd; p[1].events = POLLIN;
while(true) {
poll(p, 2, 50);
if(p[1].revents & POLLIN) {
char buf[4096];
int n = read(master_read_fd, buf, sizeof(buf));
if(n > 0) {
std::cout.write(buf, n);
output_history.insert(output_history.end(), buf, buf+n);
if(output_history.size() > MAX_HISTORY) output_history.erase(output_history.begin(), output_history.begin() + 4096);
}
}
if(p[0].revents & POLLIN) {
int client = accept(server, 0, 0);
if(client >= 0) {
char req[2048]={0};
read(client, req, 2048);
std::string s(req);
if(s.find("GET / ") != std::string::npos) {
send_http(client, HTML_PAGE, "text/html");
}
else if(s.find("GET /poll") != std::string::npos) {
size_t pos = s.find("offset=");
int off = (pos!=std::string::npos) ? std::stoi(s.substr(pos+7)) : 0;
std::string data = "";
if(off < output_history.size()) data.assign(output_history.begin()+off, output_history.end());
send_http(client, data, "text/plain");
}
else if(s.find("POST /input") != std::string::npos) {
size_t pos = s.find("\r\n\r\n");
if(pos != std::string::npos) {
std::string v = s.substr(pos+4);
if(master_write_fd != -1) write(master_write_fd, v.c_str(), v.length());
}
send_http(client, "ok", "text/plain");
}
else {
std::string err = "HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\nConnection: close\r\n\r\n";
send(client, err.c_str(), err.length(), 0);
}
close(client);
}
}
}
}