Serenity Operating System
at master 151 lines 4.1 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include "Client.h" 8#include <AK/DeprecatedString.h> 9#include <AK/HashMap.h> 10#include <AK/Types.h> 11#include <LibCore/ArgsParser.h> 12#include <LibCore/DeprecatedFile.h> 13#include <LibCore/EventLoop.h> 14#include <LibCore/Socket.h> 15#include <LibCore/TCPServer.h> 16#include <LibMain/Main.h> 17#include <fcntl.h> 18#include <stdio.h> 19#include <stdlib.h> 20#include <sys/ioctl.h> 21#include <unistd.h> 22 23static void run_command(int ptm_fd, DeprecatedString command) 24{ 25 pid_t pid = fork(); 26 if (pid == 0) { 27 char const* tty_name = ptsname(ptm_fd); 28 if (!tty_name) { 29 perror("ptsname"); 30 exit(1); 31 } 32 close(ptm_fd); 33 int pts_fd = open(tty_name, O_RDWR); 34 if (pts_fd < 0) { 35 perror("open"); 36 exit(1); 37 } 38 39 // NOTE: It's okay if this fails. 40 [[maybe_unused]] auto rc = ioctl(0, TIOCNOTTY); 41 42 close(0); 43 close(1); 44 close(2); 45 46 rc = dup2(pts_fd, 0); 47 if (rc < 0) { 48 perror("dup2"); 49 exit(1); 50 } 51 rc = dup2(pts_fd, 1); 52 if (rc < 0) { 53 perror("dup2"); 54 exit(1); 55 } 56 rc = dup2(pts_fd, 2); 57 if (rc < 0) { 58 perror("dup2"); 59 exit(1); 60 } 61 rc = close(pts_fd); 62 if (rc < 0) { 63 perror("close"); 64 exit(1); 65 } 66 rc = ioctl(0, TIOCSCTTY); 67 if (rc < 0) { 68 perror("ioctl(TIOCSCTTY)"); 69 exit(1); 70 } 71 char const* args[4] = { "/bin/Shell", nullptr, nullptr, nullptr }; 72 if (!command.is_empty()) { 73 args[1] = "-c"; 74 args[2] = command.characters(); 75 } 76 char const* envs[] = { "TERM=xterm", "PATH=" DEFAULT_PATH, nullptr }; 77 rc = execve("/bin/Shell", const_cast<char**>(args), const_cast<char**>(envs)); 78 if (rc < 0) { 79 perror("execve"); 80 exit(1); 81 } 82 VERIFY_NOT_REACHED(); 83 } 84} 85 86ErrorOr<int> serenity_main(Main::Arguments arguments) 87{ 88 int port = 23; 89 StringView command = ""sv; 90 91 Core::ArgsParser args_parser; 92 args_parser.add_option(port, "Port to listen on", nullptr, 'p', "port"); 93 args_parser.add_option(command, "Program to run on connection", nullptr, 'c', "command"); 94 args_parser.parse(arguments); 95 96 if ((u16)port != port) { 97 warnln("Invalid port number: {}", port); 98 exit(1); 99 } 100 101 Core::EventLoop event_loop; 102 auto server = TRY(Core::TCPServer::try_create()); 103 TRY(server->listen({}, port)); 104 105 HashMap<int, NonnullRefPtr<Client>> clients; 106 int next_id = 0; 107 108 server->on_ready_to_accept = [&next_id, &clients, &server, command] { 109 int id = next_id++; 110 111 auto maybe_client_socket = server->accept(); 112 if (maybe_client_socket.is_error()) { 113 warnln("accept: {}", maybe_client_socket.error()); 114 return; 115 } 116 auto client_socket = maybe_client_socket.release_value(); 117 118 int ptm_fd = posix_openpt(O_RDWR); 119 if (ptm_fd < 0) { 120 perror("posix_openpt"); 121 client_socket->close(); 122 return; 123 } 124 if (grantpt(ptm_fd) < 0) { 125 perror("grantpt"); 126 client_socket->close(); 127 return; 128 } 129 if (unlockpt(ptm_fd) < 0) { 130 perror("unlockpt"); 131 client_socket->close(); 132 return; 133 } 134 135 run_command(ptm_fd, command); 136 137 auto maybe_client = Client::create(id, move(client_socket), ptm_fd); 138 if (maybe_client.is_error()) { 139 warnln("Failed to create the client: {}", maybe_client.error()); 140 return; 141 } 142 143 auto client = maybe_client.release_value(); 144 client->on_exit = [&clients, id] { 145 Core::deferred_invoke([&clients, id] { clients.remove(id); }); 146 }; 147 clients.set(id, client); 148 }; 149 150 return event_loop.exec(); 151}