1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{linux.intel,addtoit}.com) 4 */ 5 6 #include <stdio.h> 7 #include <stdlib.h> 8 #include <errno.h> 9 #include <termios.h> 10 #include <unistd.h> 11 #include <netinet/in.h> 12 #include "chan_user.h" 13 #include <os.h> 14 #include "port.h" 15 #include <um_malloc.h> 16 17 struct port_chan { 18 int raw; 19 struct termios tt; 20 void *kernel_data; 21 char dev[sizeof("32768\0")]; 22 }; 23 24 static void *port_init(char *str, int device, const struct chan_opts *opts) 25 { 26 struct port_chan *data; 27 void *kern_data; 28 char *end; 29 int port; 30 31 if (*str != ':') { 32 printk(UM_KERN_ERR "port_init : channel type 'port' must " 33 "specify a port number\n"); 34 return NULL; 35 } 36 str++; 37 port = strtoul(str, &end, 0); 38 if ((*end != '\0') || (end == str)) { 39 printk(UM_KERN_ERR "port_init : couldn't parse port '%s'\n", 40 str); 41 return NULL; 42 } 43 44 kern_data = port_data(port); 45 if (kern_data == NULL) 46 return NULL; 47 48 data = uml_kmalloc(sizeof(*data), UM_GFP_KERNEL); 49 if (data == NULL) 50 goto err; 51 52 *data = ((struct port_chan) { .raw = opts->raw, 53 .kernel_data = kern_data }); 54 sprintf(data->dev, "%d", port); 55 56 return data; 57 err: 58 port_kern_free(kern_data); 59 return NULL; 60 } 61 62 static void port_free(void *d) 63 { 64 struct port_chan *data = d; 65 66 port_kern_free(data->kernel_data); 67 kfree(data); 68 } 69 70 static int port_open(int input, int output, int primary, void *d, 71 char **dev_out) 72 { 73 struct port_chan *data = d; 74 int fd, err; 75 76 fd = port_wait(data->kernel_data); 77 if ((fd >= 0) && data->raw) { 78 CATCH_EINTR(err = tcgetattr(fd, &data->tt)); 79 if (err) 80 return err; 81 82 err = raw(fd); 83 if (err) 84 return err; 85 } 86 *dev_out = data->dev; 87 return fd; 88 } 89 90 static void port_close(int fd, void *d) 91 { 92 struct port_chan *data = d; 93 94 port_remove_dev(data->kernel_data); 95 os_close_file(fd); 96 } 97 98 const struct chan_ops port_ops = { 99 .type = "port", 100 .init = port_init, 101 .open = port_open, 102 .close = port_close, 103 .read = generic_read, 104 .write = generic_write, 105 .console_write = generic_console_write, 106 .window_size = generic_window_size, 107 .free = port_free, 108 .winch = 1, 109 }; 110 111 int port_listen_fd(int port) 112 { 113 struct sockaddr_in addr; 114 int fd, err, arg; 115 116 fd = socket(PF_INET, SOCK_STREAM, 0); 117 if (fd == -1) 118 return -errno; 119 120 arg = 1; 121 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &arg, sizeof(arg)) < 0) { 122 err = -errno; 123 goto out; 124 } 125 126 addr.sin_family = AF_INET; 127 addr.sin_port = htons(port); 128 addr.sin_addr.s_addr = htonl(INADDR_ANY); 129 if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { 130 err = -errno; 131 goto out; 132 } 133 134 if (listen(fd, 1) < 0) { 135 err = -errno; 136 goto out; 137 } 138 139 err = os_set_fd_block(fd, 0); 140 if (err < 0) 141 goto out; 142 143 return fd; 144 out: 145 close(fd); 146 return err; 147 } 148 149 struct port_pre_exec_data { 150 int sock_fd; 151 int pipe_fd; 152 }; 153 154 static void port_pre_exec(void *arg) 155 { 156 struct port_pre_exec_data *data = arg; 157 158 dup2(data->sock_fd, 0); 159 dup2(data->sock_fd, 1); 160 dup2(data->sock_fd, 2); 161 close(data->sock_fd); 162 dup2(data->pipe_fd, 3); 163 shutdown(3, SHUT_RD); 164 close(data->pipe_fd); 165 } 166 167 int port_connection(int fd, int *socket, int *pid_out) 168 { 169 int new, err; 170 char *argv[] = { "/usr/sbin/in.telnetd", "-L", 171 OS_LIB_PATH "/uml/port-helper", NULL }; 172 struct port_pre_exec_data data; 173 174 new = accept(fd, NULL, 0); 175 if (new < 0) 176 return -errno; 177 178 err = os_pipe(socket, 0, 0); 179 if (err < 0) 180 goto out_close; 181 182 data = ((struct port_pre_exec_data) 183 { .sock_fd = new, 184 .pipe_fd = socket[1] }); 185 186 err = run_helper(port_pre_exec, &data, argv); 187 if (err < 0) 188 goto out_shutdown; 189 190 *pid_out = err; 191 return new; 192 193 out_shutdown: 194 shutdown(socket[0], SHUT_RDWR); 195 close(socket[0]); 196 shutdown(socket[1], SHUT_RDWR); 197 close(socket[1]); 198 out_close: 199 close(new); 200 return err; 201 } 202