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