162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{linux.intel,addtoit}.com) 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <stdio.h> 762306a36Sopenharmony_ci#include <stdlib.h> 862306a36Sopenharmony_ci#include <string.h> 962306a36Sopenharmony_ci#include <errno.h> 1062306a36Sopenharmony_ci#include <termios.h> 1162306a36Sopenharmony_ci#include <unistd.h> 1262306a36Sopenharmony_ci#include <netinet/in.h> 1362306a36Sopenharmony_ci#include "chan_user.h" 1462306a36Sopenharmony_ci#include <os.h> 1562306a36Sopenharmony_ci#include "port.h" 1662306a36Sopenharmony_ci#include <um_malloc.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistruct port_chan { 1962306a36Sopenharmony_ci int raw; 2062306a36Sopenharmony_ci struct termios tt; 2162306a36Sopenharmony_ci void *kernel_data; 2262306a36Sopenharmony_ci char dev[sizeof("32768\0")]; 2362306a36Sopenharmony_ci}; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic void *port_init(char *str, int device, const struct chan_opts *opts) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci struct port_chan *data; 2862306a36Sopenharmony_ci void *kern_data; 2962306a36Sopenharmony_ci char *end; 3062306a36Sopenharmony_ci int port; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci if (*str != ':') { 3362306a36Sopenharmony_ci printk(UM_KERN_ERR "port_init : channel type 'port' must " 3462306a36Sopenharmony_ci "specify a port number\n"); 3562306a36Sopenharmony_ci return NULL; 3662306a36Sopenharmony_ci } 3762306a36Sopenharmony_ci str++; 3862306a36Sopenharmony_ci port = strtoul(str, &end, 0); 3962306a36Sopenharmony_ci if ((*end != '\0') || (end == str)) { 4062306a36Sopenharmony_ci printk(UM_KERN_ERR "port_init : couldn't parse port '%s'\n", 4162306a36Sopenharmony_ci str); 4262306a36Sopenharmony_ci return NULL; 4362306a36Sopenharmony_ci } 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci kern_data = port_data(port); 4662306a36Sopenharmony_ci if (kern_data == NULL) 4762306a36Sopenharmony_ci return NULL; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci data = uml_kmalloc(sizeof(*data), UM_GFP_KERNEL); 5062306a36Sopenharmony_ci if (data == NULL) 5162306a36Sopenharmony_ci goto err; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci *data = ((struct port_chan) { .raw = opts->raw, 5462306a36Sopenharmony_ci .kernel_data = kern_data }); 5562306a36Sopenharmony_ci sprintf(data->dev, "%d", port); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci return data; 5862306a36Sopenharmony_ci err: 5962306a36Sopenharmony_ci port_kern_free(kern_data); 6062306a36Sopenharmony_ci return NULL; 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic void port_free(void *d) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci struct port_chan *data = d; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci port_kern_free(data->kernel_data); 6862306a36Sopenharmony_ci kfree(data); 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic int port_open(int input, int output, int primary, void *d, 7262306a36Sopenharmony_ci char **dev_out) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci struct port_chan *data = d; 7562306a36Sopenharmony_ci int fd, err; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci fd = port_wait(data->kernel_data); 7862306a36Sopenharmony_ci if ((fd >= 0) && data->raw) { 7962306a36Sopenharmony_ci CATCH_EINTR(err = tcgetattr(fd, &data->tt)); 8062306a36Sopenharmony_ci if (err) 8162306a36Sopenharmony_ci return err; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci err = raw(fd); 8462306a36Sopenharmony_ci if (err) 8562306a36Sopenharmony_ci return err; 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci *dev_out = data->dev; 8862306a36Sopenharmony_ci return fd; 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic void port_close(int fd, void *d) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci struct port_chan *data = d; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci port_remove_dev(data->kernel_data); 9662306a36Sopenharmony_ci os_close_file(fd); 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ciconst struct chan_ops port_ops = { 10062306a36Sopenharmony_ci .type = "port", 10162306a36Sopenharmony_ci .init = port_init, 10262306a36Sopenharmony_ci .open = port_open, 10362306a36Sopenharmony_ci .close = port_close, 10462306a36Sopenharmony_ci .read = generic_read, 10562306a36Sopenharmony_ci .write = generic_write, 10662306a36Sopenharmony_ci .console_write = generic_console_write, 10762306a36Sopenharmony_ci .window_size = generic_window_size, 10862306a36Sopenharmony_ci .free = port_free, 10962306a36Sopenharmony_ci .winch = 1, 11062306a36Sopenharmony_ci}; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ciint port_listen_fd(int port) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci struct sockaddr_in addr; 11562306a36Sopenharmony_ci int fd, err, arg; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci fd = socket(PF_INET, SOCK_STREAM, 0); 11862306a36Sopenharmony_ci if (fd == -1) 11962306a36Sopenharmony_ci return -errno; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci arg = 1; 12262306a36Sopenharmony_ci if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &arg, sizeof(arg)) < 0) { 12362306a36Sopenharmony_ci err = -errno; 12462306a36Sopenharmony_ci goto out; 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci addr.sin_family = AF_INET; 12862306a36Sopenharmony_ci addr.sin_port = htons(port); 12962306a36Sopenharmony_ci addr.sin_addr.s_addr = htonl(INADDR_ANY); 13062306a36Sopenharmony_ci if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { 13162306a36Sopenharmony_ci err = -errno; 13262306a36Sopenharmony_ci goto out; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci if (listen(fd, 1) < 0) { 13662306a36Sopenharmony_ci err = -errno; 13762306a36Sopenharmony_ci goto out; 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci err = os_set_fd_block(fd, 0); 14162306a36Sopenharmony_ci if (err < 0) 14262306a36Sopenharmony_ci goto out; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci return fd; 14562306a36Sopenharmony_ci out: 14662306a36Sopenharmony_ci close(fd); 14762306a36Sopenharmony_ci return err; 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistruct port_pre_exec_data { 15162306a36Sopenharmony_ci int sock_fd; 15262306a36Sopenharmony_ci int pipe_fd; 15362306a36Sopenharmony_ci}; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic void port_pre_exec(void *arg) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci struct port_pre_exec_data *data = arg; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci dup2(data->sock_fd, 0); 16062306a36Sopenharmony_ci dup2(data->sock_fd, 1); 16162306a36Sopenharmony_ci dup2(data->sock_fd, 2); 16262306a36Sopenharmony_ci close(data->sock_fd); 16362306a36Sopenharmony_ci dup2(data->pipe_fd, 3); 16462306a36Sopenharmony_ci shutdown(3, SHUT_RD); 16562306a36Sopenharmony_ci close(data->pipe_fd); 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ciint port_connection(int fd, int *socket, int *pid_out) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci int new, err; 17162306a36Sopenharmony_ci char *env; 17262306a36Sopenharmony_ci char *argv[] = { "in.telnetd", "-L", 17362306a36Sopenharmony_ci OS_LIB_PATH "/uml/port-helper", NULL }; 17462306a36Sopenharmony_ci struct port_pre_exec_data data; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if ((env = getenv("UML_PORT_HELPER"))) 17762306a36Sopenharmony_ci argv[2] = env; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci new = accept(fd, NULL, 0); 18062306a36Sopenharmony_ci if (new < 0) 18162306a36Sopenharmony_ci return -errno; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci err = os_access(argv[2], X_OK); 18462306a36Sopenharmony_ci if (err < 0) { 18562306a36Sopenharmony_ci printk(UM_KERN_ERR "port_connection : error accessing port-helper " 18662306a36Sopenharmony_ci "executable at %s: %s\n", argv[2], strerror(-err)); 18762306a36Sopenharmony_ci if (env == NULL) 18862306a36Sopenharmony_ci printk(UM_KERN_ERR "Set UML_PORT_HELPER environment " 18962306a36Sopenharmony_ci "variable to path to uml-utilities port-helper " 19062306a36Sopenharmony_ci "binary\n"); 19162306a36Sopenharmony_ci goto out_close; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci err = os_pipe(socket, 0, 0); 19562306a36Sopenharmony_ci if (err < 0) 19662306a36Sopenharmony_ci goto out_close; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci data = ((struct port_pre_exec_data) 19962306a36Sopenharmony_ci { .sock_fd = new, 20062306a36Sopenharmony_ci .pipe_fd = socket[1] }); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci err = run_helper(port_pre_exec, &data, argv); 20362306a36Sopenharmony_ci if (err < 0) 20462306a36Sopenharmony_ci goto out_shutdown; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci *pid_out = err; 20762306a36Sopenharmony_ci return new; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci out_shutdown: 21062306a36Sopenharmony_ci shutdown(socket[0], SHUT_RDWR); 21162306a36Sopenharmony_ci close(socket[0]); 21262306a36Sopenharmony_ci shutdown(socket[1], SHUT_RDWR); 21362306a36Sopenharmony_ci close(socket[1]); 21462306a36Sopenharmony_ci out_close: 21562306a36Sopenharmony_ci close(new); 21662306a36Sopenharmony_ci return err; 21762306a36Sopenharmony_ci} 218