blob: ed4a1a6c5d83fa03fb515f69a2fc30340f596301 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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 "user_util.h"
17#include "kern_util.h"
18#include "user.h"
19#include "chan_user.h"
20#include "port.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070021#include "os.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, 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
103static int port_console_write(int fd, const char *buf, int n, void *d)
104{
105 struct port_chan *data = d;
106
107 return(generic_console_write(fd, buf, n, &data->tt));
108}
109
110struct chan_ops port_ops = {
111 .type = "port",
112 .init = port_init,
113 .open = port_open,
114 .close = port_close,
115 .read = generic_read,
116 .write = generic_write,
117 .console_write = port_console_write,
118 .window_size = generic_window_size,
119 .free = port_free,
120 .winch = 1,
121};
122
123int port_listen_fd(int port)
124{
125 struct sockaddr_in addr;
126 int fd, err, arg;
127
128 fd = socket(PF_INET, SOCK_STREAM, 0);
129 if(fd == -1)
130 return(-errno);
131
132 arg = 1;
133 if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &arg, sizeof(arg)) < 0){
134 err = -errno;
135 goto out;
136 }
137
138 addr.sin_family = AF_INET;
139 addr.sin_port = htons(port);
140 addr.sin_addr.s_addr = htonl(INADDR_ANY);
141 if(bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0){
142 err = -errno;
143 goto out;
144 }
145
146 if(listen(fd, 1) < 0){
147 err = -errno;
148 goto out;
149 }
150
151 err = os_set_fd_block(fd, 0);
152 if(err < 0)
153 goto out;
154
155 return(fd);
156 out:
157 os_close_file(fd);
158 return(err);
159}
160
161struct port_pre_exec_data {
162 int sock_fd;
163 int pipe_fd;
164};
165
166void port_pre_exec(void *arg)
167{
168 struct port_pre_exec_data *data = arg;
169
170 dup2(data->sock_fd, 0);
171 dup2(data->sock_fd, 1);
172 dup2(data->sock_fd, 2);
173 os_close_file(data->sock_fd);
174 dup2(data->pipe_fd, 3);
175 os_shutdown_socket(3, 1, 0);
176 os_close_file(data->pipe_fd);
177}
178
179int port_connection(int fd, int *socket, int *pid_out)
180{
181 int new, err;
182 char *argv[] = { "/usr/sbin/in.telnetd", "-L",
183 "/usr/lib/uml/port-helper", NULL };
184 struct port_pre_exec_data data;
185
186 new = os_accept_connection(fd);
187 if(new < 0)
188 return(new);
189
190 err = os_pipe(socket, 0, 0);
191 if(err < 0)
192 goto out_close;
193
194 data = ((struct port_pre_exec_data)
195 { .sock_fd = new,
196 .pipe_fd = socket[1] });
197
198 err = run_helper(port_pre_exec, &data, argv, NULL);
199 if(err < 0)
200 goto out_shutdown;
201
202 *pid_out = err;
203 return(new);
204
205 out_shutdown:
206 os_shutdown_socket(socket[0], 1, 1);
207 os_close_file(socket[0]);
208 os_shutdown_socket(socket[1], 1, 1);
209 os_close_file(socket[1]);
210 out_close:
211 os_close_file(new);
212 return(err);
213}
214
215/*
216 * Overrides for Emacs so that we follow Linus's tabbing style.
217 * Emacs will notice this stuff at the end of the file and automatically
218 * adjust the settings for this buffer only. This must remain at the end
219 * of the file.
220 * ---------------------------------------------------------------------------
221 * Local variables:
222 * c-file-style: "linux"
223 * End:
224 */