18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{linux.intel,addtoit}.com) 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <stdio.h> 78c2ecf20Sopenharmony_ci#include <stdlib.h> 88c2ecf20Sopenharmony_ci#include <errno.h> 98c2ecf20Sopenharmony_ci#include <termios.h> 108c2ecf20Sopenharmony_ci#include <unistd.h> 118c2ecf20Sopenharmony_ci#include <netinet/in.h> 128c2ecf20Sopenharmony_ci#include "chan_user.h" 138c2ecf20Sopenharmony_ci#include <os.h> 148c2ecf20Sopenharmony_ci#include "port.h" 158c2ecf20Sopenharmony_ci#include <um_malloc.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_cistruct port_chan { 188c2ecf20Sopenharmony_ci int raw; 198c2ecf20Sopenharmony_ci struct termios tt; 208c2ecf20Sopenharmony_ci void *kernel_data; 218c2ecf20Sopenharmony_ci char dev[sizeof("32768\0")]; 228c2ecf20Sopenharmony_ci}; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic void *port_init(char *str, int device, const struct chan_opts *opts) 258c2ecf20Sopenharmony_ci{ 268c2ecf20Sopenharmony_ci struct port_chan *data; 278c2ecf20Sopenharmony_ci void *kern_data; 288c2ecf20Sopenharmony_ci char *end; 298c2ecf20Sopenharmony_ci int port; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci if (*str != ':') { 328c2ecf20Sopenharmony_ci printk(UM_KERN_ERR "port_init : channel type 'port' must " 338c2ecf20Sopenharmony_ci "specify a port number\n"); 348c2ecf20Sopenharmony_ci return NULL; 358c2ecf20Sopenharmony_ci } 368c2ecf20Sopenharmony_ci str++; 378c2ecf20Sopenharmony_ci port = strtoul(str, &end, 0); 388c2ecf20Sopenharmony_ci if ((*end != '\0') || (end == str)) { 398c2ecf20Sopenharmony_ci printk(UM_KERN_ERR "port_init : couldn't parse port '%s'\n", 408c2ecf20Sopenharmony_ci str); 418c2ecf20Sopenharmony_ci return NULL; 428c2ecf20Sopenharmony_ci } 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci kern_data = port_data(port); 458c2ecf20Sopenharmony_ci if (kern_data == NULL) 468c2ecf20Sopenharmony_ci return NULL; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci data = uml_kmalloc(sizeof(*data), UM_GFP_KERNEL); 498c2ecf20Sopenharmony_ci if (data == NULL) 508c2ecf20Sopenharmony_ci goto err; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci *data = ((struct port_chan) { .raw = opts->raw, 538c2ecf20Sopenharmony_ci .kernel_data = kern_data }); 548c2ecf20Sopenharmony_ci sprintf(data->dev, "%d", port); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci return data; 578c2ecf20Sopenharmony_ci err: 588c2ecf20Sopenharmony_ci port_kern_free(kern_data); 598c2ecf20Sopenharmony_ci return NULL; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic void port_free(void *d) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci struct port_chan *data = d; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci port_kern_free(data->kernel_data); 678c2ecf20Sopenharmony_ci kfree(data); 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic int port_open(int input, int output, int primary, void *d, 718c2ecf20Sopenharmony_ci char **dev_out) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci struct port_chan *data = d; 748c2ecf20Sopenharmony_ci int fd, err; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci fd = port_wait(data->kernel_data); 778c2ecf20Sopenharmony_ci if ((fd >= 0) && data->raw) { 788c2ecf20Sopenharmony_ci CATCH_EINTR(err = tcgetattr(fd, &data->tt)); 798c2ecf20Sopenharmony_ci if (err) 808c2ecf20Sopenharmony_ci return err; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci err = raw(fd); 838c2ecf20Sopenharmony_ci if (err) 848c2ecf20Sopenharmony_ci return err; 858c2ecf20Sopenharmony_ci } 868c2ecf20Sopenharmony_ci *dev_out = data->dev; 878c2ecf20Sopenharmony_ci return fd; 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic void port_close(int fd, void *d) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci struct port_chan *data = d; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci port_remove_dev(data->kernel_data); 958c2ecf20Sopenharmony_ci os_close_file(fd); 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ciconst struct chan_ops port_ops = { 998c2ecf20Sopenharmony_ci .type = "port", 1008c2ecf20Sopenharmony_ci .init = port_init, 1018c2ecf20Sopenharmony_ci .open = port_open, 1028c2ecf20Sopenharmony_ci .close = port_close, 1038c2ecf20Sopenharmony_ci .read = generic_read, 1048c2ecf20Sopenharmony_ci .write = generic_write, 1058c2ecf20Sopenharmony_ci .console_write = generic_console_write, 1068c2ecf20Sopenharmony_ci .window_size = generic_window_size, 1078c2ecf20Sopenharmony_ci .free = port_free, 1088c2ecf20Sopenharmony_ci .winch = 1, 1098c2ecf20Sopenharmony_ci}; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ciint port_listen_fd(int port) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci struct sockaddr_in addr; 1148c2ecf20Sopenharmony_ci int fd, err, arg; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci fd = socket(PF_INET, SOCK_STREAM, 0); 1178c2ecf20Sopenharmony_ci if (fd == -1) 1188c2ecf20Sopenharmony_ci return -errno; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci arg = 1; 1218c2ecf20Sopenharmony_ci if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &arg, sizeof(arg)) < 0) { 1228c2ecf20Sopenharmony_ci err = -errno; 1238c2ecf20Sopenharmony_ci goto out; 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci addr.sin_family = AF_INET; 1278c2ecf20Sopenharmony_ci addr.sin_port = htons(port); 1288c2ecf20Sopenharmony_ci addr.sin_addr.s_addr = htonl(INADDR_ANY); 1298c2ecf20Sopenharmony_ci if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { 1308c2ecf20Sopenharmony_ci err = -errno; 1318c2ecf20Sopenharmony_ci goto out; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci if (listen(fd, 1) < 0) { 1358c2ecf20Sopenharmony_ci err = -errno; 1368c2ecf20Sopenharmony_ci goto out; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci err = os_set_fd_block(fd, 0); 1408c2ecf20Sopenharmony_ci if (err < 0) 1418c2ecf20Sopenharmony_ci goto out; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci return fd; 1448c2ecf20Sopenharmony_ci out: 1458c2ecf20Sopenharmony_ci close(fd); 1468c2ecf20Sopenharmony_ci return err; 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistruct port_pre_exec_data { 1508c2ecf20Sopenharmony_ci int sock_fd; 1518c2ecf20Sopenharmony_ci int pipe_fd; 1528c2ecf20Sopenharmony_ci}; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic void port_pre_exec(void *arg) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci struct port_pre_exec_data *data = arg; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci dup2(data->sock_fd, 0); 1598c2ecf20Sopenharmony_ci dup2(data->sock_fd, 1); 1608c2ecf20Sopenharmony_ci dup2(data->sock_fd, 2); 1618c2ecf20Sopenharmony_ci close(data->sock_fd); 1628c2ecf20Sopenharmony_ci dup2(data->pipe_fd, 3); 1638c2ecf20Sopenharmony_ci shutdown(3, SHUT_RD); 1648c2ecf20Sopenharmony_ci close(data->pipe_fd); 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ciint port_connection(int fd, int *socket, int *pid_out) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci int new, err; 1708c2ecf20Sopenharmony_ci char *argv[] = { "/usr/sbin/in.telnetd", "-L", 1718c2ecf20Sopenharmony_ci OS_LIB_PATH "/uml/port-helper", NULL }; 1728c2ecf20Sopenharmony_ci struct port_pre_exec_data data; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci new = accept(fd, NULL, 0); 1758c2ecf20Sopenharmony_ci if (new < 0) 1768c2ecf20Sopenharmony_ci return -errno; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci err = os_pipe(socket, 0, 0); 1798c2ecf20Sopenharmony_ci if (err < 0) 1808c2ecf20Sopenharmony_ci goto out_close; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci data = ((struct port_pre_exec_data) 1838c2ecf20Sopenharmony_ci { .sock_fd = new, 1848c2ecf20Sopenharmony_ci .pipe_fd = socket[1] }); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci err = run_helper(port_pre_exec, &data, argv); 1878c2ecf20Sopenharmony_ci if (err < 0) 1888c2ecf20Sopenharmony_ci goto out_shutdown; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci *pid_out = err; 1918c2ecf20Sopenharmony_ci return new; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci out_shutdown: 1948c2ecf20Sopenharmony_ci shutdown(socket[0], SHUT_RDWR); 1958c2ecf20Sopenharmony_ci close(socket[0]); 1968c2ecf20Sopenharmony_ci shutdown(socket[1], SHUT_RDWR); 1978c2ecf20Sopenharmony_ci close(socket[1]); 1988c2ecf20Sopenharmony_ci out_close: 1998c2ecf20Sopenharmony_ci close(new); 2008c2ecf20Sopenharmony_ci return err; 2018c2ecf20Sopenharmony_ci} 202