1dbddf429SAlex Dewar // SPDX-License-Identifier: GPL-2.0 21da177e4SLinus Torvalds /* 3e99525f9SJeff Dike * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{linux.intel,addtoit}.com) 41da177e4SLinus Torvalds */ 51da177e4SLinus Torvalds 61da177e4SLinus Torvalds #include <stdio.h> 71da177e4SLinus Torvalds #include <stdlib.h> 8*3cb5a7f1SGlenn Washburn #include <string.h> 91da177e4SLinus Torvalds #include <errno.h> 101da177e4SLinus Torvalds #include <termios.h> 11e99525f9SJeff Dike #include <unistd.h> 121da177e4SLinus Torvalds #include <netinet/in.h> 131da177e4SLinus Torvalds #include "chan_user.h" 1437185b33SAl Viro #include <os.h> 15e99525f9SJeff Dike #include "port.h" 1637185b33SAl Viro #include <um_malloc.h> 171da177e4SLinus Torvalds 181da177e4SLinus Torvalds struct port_chan { 191da177e4SLinus Torvalds int raw; 201da177e4SLinus Torvalds struct termios tt; 211da177e4SLinus Torvalds void *kernel_data; 221da177e4SLinus Torvalds char dev[sizeof("32768\0")]; 231da177e4SLinus Torvalds }; 241da177e4SLinus Torvalds 255e7672ecSJeff Dike static void *port_init(char *str, int device, const struct chan_opts *opts) 261da177e4SLinus Torvalds { 271da177e4SLinus Torvalds struct port_chan *data; 281da177e4SLinus Torvalds void *kern_data; 291da177e4SLinus Torvalds char *end; 301da177e4SLinus Torvalds int port; 311da177e4SLinus Torvalds 321da177e4SLinus Torvalds if (*str != ':') { 33e99525f9SJeff Dike printk(UM_KERN_ERR "port_init : channel type 'port' must " 34e99525f9SJeff Dike "specify a port number\n"); 3567608e0cSJeff Dike return NULL; 361da177e4SLinus Torvalds } 371da177e4SLinus Torvalds str++; 381da177e4SLinus Torvalds port = strtoul(str, &end, 0); 391da177e4SLinus Torvalds if ((*end != '\0') || (end == str)) { 40e99525f9SJeff Dike printk(UM_KERN_ERR "port_init : couldn't parse port '%s'\n", 41e99525f9SJeff Dike str); 4267608e0cSJeff Dike return NULL; 431da177e4SLinus Torvalds } 441da177e4SLinus Torvalds 451da177e4SLinus Torvalds kern_data = port_data(port); 461da177e4SLinus Torvalds if (kern_data == NULL) 4767608e0cSJeff Dike return NULL; 481da177e4SLinus Torvalds 4943f5b308SJeff Dike data = uml_kmalloc(sizeof(*data), UM_GFP_KERNEL); 501da177e4SLinus Torvalds if (data == NULL) 511da177e4SLinus Torvalds goto err; 521da177e4SLinus Torvalds 531da177e4SLinus Torvalds *data = ((struct port_chan) { .raw = opts->raw, 541da177e4SLinus Torvalds .kernel_data = kern_data }); 551da177e4SLinus Torvalds sprintf(data->dev, "%d", port); 561da177e4SLinus Torvalds 5767608e0cSJeff Dike return data; 581da177e4SLinus Torvalds err: 591da177e4SLinus Torvalds port_kern_free(kern_data); 6067608e0cSJeff Dike return NULL; 611da177e4SLinus Torvalds } 621da177e4SLinus Torvalds 631da177e4SLinus Torvalds static void port_free(void *d) 641da177e4SLinus Torvalds { 651da177e4SLinus Torvalds struct port_chan *data = d; 661da177e4SLinus Torvalds 671da177e4SLinus Torvalds port_kern_free(data->kernel_data); 681da177e4SLinus Torvalds kfree(data); 691da177e4SLinus Torvalds } 701da177e4SLinus Torvalds 711da177e4SLinus Torvalds static int port_open(int input, int output, int primary, void *d, 721da177e4SLinus Torvalds char **dev_out) 731da177e4SLinus Torvalds { 741da177e4SLinus Torvalds struct port_chan *data = d; 751da177e4SLinus Torvalds int fd, err; 761da177e4SLinus Torvalds 771da177e4SLinus Torvalds fd = port_wait(data->kernel_data); 781da177e4SLinus Torvalds if ((fd >= 0) && data->raw) { 791da177e4SLinus Torvalds CATCH_EINTR(err = tcgetattr(fd, &data->tt)); 801da177e4SLinus Torvalds if (err) 8167608e0cSJeff Dike return err; 821da177e4SLinus Torvalds 831da177e4SLinus Torvalds err = raw(fd); 841da177e4SLinus Torvalds if (err) 8567608e0cSJeff Dike return err; 861da177e4SLinus Torvalds } 871da177e4SLinus Torvalds *dev_out = data->dev; 8867608e0cSJeff Dike return fd; 891da177e4SLinus Torvalds } 901da177e4SLinus Torvalds 911da177e4SLinus Torvalds static void port_close(int fd, void *d) 921da177e4SLinus Torvalds { 931da177e4SLinus Torvalds struct port_chan *data = d; 941da177e4SLinus Torvalds 951da177e4SLinus Torvalds port_remove_dev(data->kernel_data); 961da177e4SLinus Torvalds os_close_file(fd); 971da177e4SLinus Torvalds } 981da177e4SLinus Torvalds 995e7672ecSJeff Dike const struct chan_ops port_ops = { 1001da177e4SLinus Torvalds .type = "port", 1011da177e4SLinus Torvalds .init = port_init, 1021da177e4SLinus Torvalds .open = port_open, 1031da177e4SLinus Torvalds .close = port_close, 1041da177e4SLinus Torvalds .read = generic_read, 1051da177e4SLinus Torvalds .write = generic_write, 106fd9bc53bSPaolo 'Blaisorblade' Giarrusso .console_write = generic_console_write, 1071da177e4SLinus Torvalds .window_size = generic_window_size, 1081da177e4SLinus Torvalds .free = port_free, 1091da177e4SLinus Torvalds .winch = 1, 1101da177e4SLinus Torvalds }; 1111da177e4SLinus Torvalds 1121da177e4SLinus Torvalds int port_listen_fd(int port) 1131da177e4SLinus Torvalds { 1141da177e4SLinus Torvalds struct sockaddr_in addr; 1151da177e4SLinus Torvalds int fd, err, arg; 1161da177e4SLinus Torvalds 1171da177e4SLinus Torvalds fd = socket(PF_INET, SOCK_STREAM, 0); 1181da177e4SLinus Torvalds if (fd == -1) 11967608e0cSJeff Dike return -errno; 1201da177e4SLinus Torvalds 1211da177e4SLinus Torvalds arg = 1; 1221da177e4SLinus Torvalds if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &arg, sizeof(arg)) < 0) { 1231da177e4SLinus Torvalds err = -errno; 1241da177e4SLinus Torvalds goto out; 1251da177e4SLinus Torvalds } 1261da177e4SLinus Torvalds 1271da177e4SLinus Torvalds addr.sin_family = AF_INET; 1281da177e4SLinus Torvalds addr.sin_port = htons(port); 1291da177e4SLinus Torvalds addr.sin_addr.s_addr = htonl(INADDR_ANY); 1301da177e4SLinus Torvalds if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { 1311da177e4SLinus Torvalds err = -errno; 1321da177e4SLinus Torvalds goto out; 1331da177e4SLinus Torvalds } 1341da177e4SLinus Torvalds 1351da177e4SLinus Torvalds if (listen(fd, 1) < 0) { 1361da177e4SLinus Torvalds err = -errno; 1371da177e4SLinus Torvalds goto out; 1381da177e4SLinus Torvalds } 1391da177e4SLinus Torvalds 1401da177e4SLinus Torvalds err = os_set_fd_block(fd, 0); 1411da177e4SLinus Torvalds if (err < 0) 1421da177e4SLinus Torvalds goto out; 1431da177e4SLinus Torvalds 14467608e0cSJeff Dike return fd; 1451da177e4SLinus Torvalds out: 146e99525f9SJeff Dike close(fd); 14767608e0cSJeff Dike return err; 1481da177e4SLinus Torvalds } 1491da177e4SLinus Torvalds 1501da177e4SLinus Torvalds struct port_pre_exec_data { 1511da177e4SLinus Torvalds int sock_fd; 1521da177e4SLinus Torvalds int pipe_fd; 1531da177e4SLinus Torvalds }; 1541da177e4SLinus Torvalds 1551605ec04SWANG Cong static void port_pre_exec(void *arg) 1561da177e4SLinus Torvalds { 1571da177e4SLinus Torvalds struct port_pre_exec_data *data = arg; 1581da177e4SLinus Torvalds 1591da177e4SLinus Torvalds dup2(data->sock_fd, 0); 1601da177e4SLinus Torvalds dup2(data->sock_fd, 1); 1611da177e4SLinus Torvalds dup2(data->sock_fd, 2); 162e99525f9SJeff Dike close(data->sock_fd); 1631da177e4SLinus Torvalds dup2(data->pipe_fd, 3); 164e99525f9SJeff Dike shutdown(3, SHUT_RD); 165e99525f9SJeff Dike close(data->pipe_fd); 1661da177e4SLinus Torvalds } 1671da177e4SLinus Torvalds 1681da177e4SLinus Torvalds int port_connection(int fd, int *socket, int *pid_out) 1691da177e4SLinus Torvalds { 1701da177e4SLinus Torvalds int new, err; 171db8109a8SGlenn Washburn char *env; 1726e8f4b70SGlenn Washburn char *argv[] = { "in.telnetd", "-L", 1739ca19a3aSRitesh Raj Sarraf OS_LIB_PATH "/uml/port-helper", NULL }; 1741da177e4SLinus Torvalds struct port_pre_exec_data data; 1751da177e4SLinus Torvalds 176db8109a8SGlenn Washburn if ((env = getenv("UML_PORT_HELPER"))) 177db8109a8SGlenn Washburn argv[2] = env; 178db8109a8SGlenn Washburn 179e99525f9SJeff Dike new = accept(fd, NULL, 0); 1801da177e4SLinus Torvalds if (new < 0) 181e99525f9SJeff Dike return -errno; 1821da177e4SLinus Torvalds 183*3cb5a7f1SGlenn Washburn err = os_access(argv[2], X_OK); 184*3cb5a7f1SGlenn Washburn if (err < 0) { 185*3cb5a7f1SGlenn Washburn printk(UM_KERN_ERR "port_connection : error accessing port-helper " 186*3cb5a7f1SGlenn Washburn "executable at %s: %s\n", argv[2], strerror(-err)); 187*3cb5a7f1SGlenn Washburn if (env == NULL) 188*3cb5a7f1SGlenn Washburn printk(UM_KERN_ERR "Set UML_PORT_HELPER environment " 189*3cb5a7f1SGlenn Washburn "variable to path to uml-utilities port-helper " 190*3cb5a7f1SGlenn Washburn "binary\n"); 191*3cb5a7f1SGlenn Washburn goto out_close; 192*3cb5a7f1SGlenn Washburn } 193*3cb5a7f1SGlenn Washburn 1941da177e4SLinus Torvalds err = os_pipe(socket, 0, 0); 1951da177e4SLinus Torvalds if (err < 0) 1961da177e4SLinus Torvalds goto out_close; 1971da177e4SLinus Torvalds 1981da177e4SLinus Torvalds data = ((struct port_pre_exec_data) 1991da177e4SLinus Torvalds { .sock_fd = new, 2001da177e4SLinus Torvalds .pipe_fd = socket[1] }); 2011da177e4SLinus Torvalds 202c4399016SJeff Dike err = run_helper(port_pre_exec, &data, argv); 2031da177e4SLinus Torvalds if (err < 0) 2041da177e4SLinus Torvalds goto out_shutdown; 2051da177e4SLinus Torvalds 2061da177e4SLinus Torvalds *pid_out = err; 20767608e0cSJeff Dike return new; 2081da177e4SLinus Torvalds 2091da177e4SLinus Torvalds out_shutdown: 210e99525f9SJeff Dike shutdown(socket[0], SHUT_RDWR); 211e99525f9SJeff Dike close(socket[0]); 212e99525f9SJeff Dike shutdown(socket[1], SHUT_RDWR); 213e99525f9SJeff Dike close(socket[1]); 2141da177e4SLinus Torvalds out_close: 215e99525f9SJeff Dike close(new); 21667608e0cSJeff Dike return err; 2171da177e4SLinus Torvalds } 218