mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
201 lines
3.7 KiB
201 lines
3.7 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
/* |
|
* Copyright (C) 2001 - 2007 Jeff Dike (jdike@{linux.intel,addtoit}.com) |
|
*/ |
|
|
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <errno.h> |
|
#include <termios.h> |
|
#include <unistd.h> |
|
#include <netinet/in.h> |
|
#include "chan_user.h" |
|
#include <os.h> |
|
#include "port.h" |
|
#include <um_malloc.h> |
|
|
|
struct port_chan { |
|
int raw; |
|
struct termios tt; |
|
void *kernel_data; |
|
char dev[sizeof("32768\0")]; |
|
}; |
|
|
|
static void *port_init(char *str, int device, const struct chan_opts *opts) |
|
{ |
|
struct port_chan *data; |
|
void *kern_data; |
|
char *end; |
|
int port; |
|
|
|
if (*str != ':') { |
|
printk(UM_KERN_ERR "port_init : channel type 'port' must " |
|
"specify a port number\n"); |
|
return NULL; |
|
} |
|
str++; |
|
port = strtoul(str, &end, 0); |
|
if ((*end != '\0') || (end == str)) { |
|
printk(UM_KERN_ERR "port_init : couldn't parse port '%s'\n", |
|
str); |
|
return NULL; |
|
} |
|
|
|
kern_data = port_data(port); |
|
if (kern_data == NULL) |
|
return NULL; |
|
|
|
data = uml_kmalloc(sizeof(*data), UM_GFP_KERNEL); |
|
if (data == NULL) |
|
goto err; |
|
|
|
*data = ((struct port_chan) { .raw = opts->raw, |
|
.kernel_data = kern_data }); |
|
sprintf(data->dev, "%d", port); |
|
|
|
return data; |
|
err: |
|
port_kern_free(kern_data); |
|
return NULL; |
|
} |
|
|
|
static void port_free(void *d) |
|
{ |
|
struct port_chan *data = d; |
|
|
|
port_kern_free(data->kernel_data); |
|
kfree(data); |
|
} |
|
|
|
static int port_open(int input, int output, int primary, void *d, |
|
char **dev_out) |
|
{ |
|
struct port_chan *data = d; |
|
int fd, err; |
|
|
|
fd = port_wait(data->kernel_data); |
|
if ((fd >= 0) && data->raw) { |
|
CATCH_EINTR(err = tcgetattr(fd, &data->tt)); |
|
if (err) |
|
return err; |
|
|
|
err = raw(fd); |
|
if (err) |
|
return err; |
|
} |
|
*dev_out = data->dev; |
|
return fd; |
|
} |
|
|
|
static void port_close(int fd, void *d) |
|
{ |
|
struct port_chan *data = d; |
|
|
|
port_remove_dev(data->kernel_data); |
|
os_close_file(fd); |
|
} |
|
|
|
const struct chan_ops port_ops = { |
|
.type = "port", |
|
.init = port_init, |
|
.open = port_open, |
|
.close = port_close, |
|
.read = generic_read, |
|
.write = generic_write, |
|
.console_write = generic_console_write, |
|
.window_size = generic_window_size, |
|
.free = port_free, |
|
.winch = 1, |
|
}; |
|
|
|
int port_listen_fd(int port) |
|
{ |
|
struct sockaddr_in addr; |
|
int fd, err, arg; |
|
|
|
fd = socket(PF_INET, SOCK_STREAM, 0); |
|
if (fd == -1) |
|
return -errno; |
|
|
|
arg = 1; |
|
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &arg, sizeof(arg)) < 0) { |
|
err = -errno; |
|
goto out; |
|
} |
|
|
|
addr.sin_family = AF_INET; |
|
addr.sin_port = htons(port); |
|
addr.sin_addr.s_addr = htonl(INADDR_ANY); |
|
if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { |
|
err = -errno; |
|
goto out; |
|
} |
|
|
|
if (listen(fd, 1) < 0) { |
|
err = -errno; |
|
goto out; |
|
} |
|
|
|
err = os_set_fd_block(fd, 0); |
|
if (err < 0) |
|
goto out; |
|
|
|
return fd; |
|
out: |
|
close(fd); |
|
return err; |
|
} |
|
|
|
struct port_pre_exec_data { |
|
int sock_fd; |
|
int pipe_fd; |
|
}; |
|
|
|
static void port_pre_exec(void *arg) |
|
{ |
|
struct port_pre_exec_data *data = arg; |
|
|
|
dup2(data->sock_fd, 0); |
|
dup2(data->sock_fd, 1); |
|
dup2(data->sock_fd, 2); |
|
close(data->sock_fd); |
|
dup2(data->pipe_fd, 3); |
|
shutdown(3, SHUT_RD); |
|
close(data->pipe_fd); |
|
} |
|
|
|
int port_connection(int fd, int *socket, int *pid_out) |
|
{ |
|
int new, err; |
|
char *argv[] = { "/usr/sbin/in.telnetd", "-L", |
|
OS_LIB_PATH "/uml/port-helper", NULL }; |
|
struct port_pre_exec_data data; |
|
|
|
new = accept(fd, NULL, 0); |
|
if (new < 0) |
|
return -errno; |
|
|
|
err = os_pipe(socket, 0, 0); |
|
if (err < 0) |
|
goto out_close; |
|
|
|
data = ((struct port_pre_exec_data) |
|
{ .sock_fd = new, |
|
.pipe_fd = socket[1] }); |
|
|
|
err = run_helper(port_pre_exec, &data, argv); |
|
if (err < 0) |
|
goto out_shutdown; |
|
|
|
*pid_out = err; |
|
return new; |
|
|
|
out_shutdown: |
|
shutdown(socket[0], SHUT_RDWR); |
|
close(socket[0]); |
|
shutdown(socket[1], SHUT_RDWR); |
|
close(socket[1]); |
|
out_close: |
|
close(new); |
|
return err; |
|
}
|
|
|