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