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 "user_util.h" 17 #include "kern_util.h" 18 #include "user.h" 19 #include "chan_user.h" 20 #include "port.h" 21 #include "os.h" 22 23 struct port_chan { 24 int raw; 25 struct termios tt; 26 void *kernel_data; 27 char dev[sizeof("32768\0")]; 28 }; 29 30 static 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 67 static 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 75 static 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 95 static 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 103 static 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 110 struct 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 123 int 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 161 struct port_pre_exec_data { 162 int sock_fd; 163 int pipe_fd; 164 }; 165 166 void 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 179 int 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 */ 225