Spaces:
Sleeping
Sleeping
| 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); | |
| } | |
| } | |
| } | |
| } |