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