Merge rsync://rsync.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[linux-2.6] / arch / um / drivers / net_user.c
1 /* 
2  * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
3  * Licensed under the GPL
4  */
5
6 #include <stddef.h>
7 #include <stdarg.h>
8 #include <unistd.h>
9 #include <stdio.h>
10 #include <errno.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <sys/socket.h>
14 #include <sys/wait.h>
15 #include "user.h"
16 #include "user_util.h"
17 #include "kern_util.h"
18 #include "net_user.h"
19 #include "os.h"
20
21 int tap_open_common(void *dev, char *gate_addr)
22 {
23         int tap_addr[4];
24
25         if(gate_addr == NULL)
26                 return 0;
27         if(sscanf(gate_addr, "%d.%d.%d.%d", &tap_addr[0], 
28                   &tap_addr[1], &tap_addr[2], &tap_addr[3]) != 4){
29                 printk("Invalid tap IP address - '%s'\n", gate_addr);
30                 return -EINVAL;
31         }
32         return 0;
33 }
34
35 void tap_check_ips(char *gate_addr, unsigned char *eth_addr)
36 {
37         int tap_addr[4];
38
39         if((gate_addr != NULL) && 
40            (sscanf(gate_addr, "%d.%d.%d.%d", &tap_addr[0], 
41                    &tap_addr[1], &tap_addr[2], &tap_addr[3]) == 4) &&
42            (eth_addr[0] == tap_addr[0]) && 
43            (eth_addr[1] == tap_addr[1]) && 
44            (eth_addr[2] == tap_addr[2]) && 
45            (eth_addr[3] == tap_addr[3])){
46                 printk("The tap IP address and the UML eth IP address"
47                        " must be different\n");
48         }
49 }
50
51 /* Do reliable error handling as this fails frequently enough. */
52 void read_output(int fd, char *output, int len)
53 {
54         int remain, ret, expected;
55         char c;
56         char *str;
57
58         if(output == NULL){
59                 output = &c;
60                 len = sizeof(c);
61         }
62                 
63         *output = '\0';
64         ret = os_read_file(fd, &remain, sizeof(remain));
65
66         if (ret != sizeof(remain)) {
67                 expected = sizeof(remain);
68                 str = "length";
69                 goto err;
70         }
71
72         while(remain != 0){
73                 expected = (remain < len) ? remain : len;
74                 ret = os_read_file(fd, output, expected);
75                 if (ret != expected) {
76                         str = "data";
77                         goto err;
78                 }
79                 remain -= ret;
80         }
81
82         return;
83
84 err:
85         if (ret < 0)
86                 printk("read_output - read of %s failed, errno = %d\n", str, -ret);
87         else
88                 printk("read_output - read of %s failed, read only %d of %d bytes\n", str, ret, expected);
89 }
90
91 int net_read(int fd, void *buf, int len)
92 {
93         int n;
94
95         n = os_read_file(fd,  buf,  len);
96
97         if(n == -EAGAIN)
98                 return 0;
99         else if(n == 0)
100                 return -ENOTCONN;
101         return n;
102 }
103
104 int net_recvfrom(int fd, void *buf, int len)
105 {
106         int n;
107
108         CATCH_EINTR(n = recvfrom(fd,  buf,  len, 0, NULL, NULL));
109         if(n < 0){
110                 if(errno == EAGAIN)
111                         return 0;
112                 return -errno;
113         }
114         else if(n == 0)
115                 return -ENOTCONN;
116         return n;
117 }
118
119 int net_write(int fd, void *buf, int len)
120 {
121         int n;
122
123         n = os_write_file(fd, buf, len);
124
125         if(n == -EAGAIN)
126                 return 0;
127         else if(n == 0)
128                 return -ENOTCONN;
129         return n;
130 }
131
132 int net_send(int fd, void *buf, int len)
133 {
134         int n;
135
136         CATCH_EINTR(n = send(fd, buf, len, 0));
137         if(n < 0){
138                 if(errno == EAGAIN)
139                         return 0;
140                 return -errno;
141         }
142         else if(n == 0)
143                 return -ENOTCONN;
144         return n;
145 }
146
147 int net_sendto(int fd, void *buf, int len, void *to, int sock_len)
148 {
149         int n;
150
151         CATCH_EINTR(n = sendto(fd, buf, len, 0, (struct sockaddr *) to,
152                                sock_len));
153         if(n < 0){
154                 if(errno == EAGAIN)
155                         return 0;
156                 return -errno;
157         }
158         else if(n == 0)
159                 return -ENOTCONN;
160         return n;
161 }
162
163 struct change_pre_exec_data {
164         int close_me;
165         int stdout;
166 };
167
168 static void change_pre_exec(void *arg)
169 {
170         struct change_pre_exec_data *data = arg;
171
172         os_close_file(data->close_me);
173         dup2(data->stdout, 1);
174 }
175
176 static int change_tramp(char **argv, char *output, int output_len)
177 {
178         int pid, fds[2], err;
179         struct change_pre_exec_data pe_data;
180
181         err = os_pipe(fds, 1, 0);
182         if(err < 0){
183                 printk("change_tramp - pipe failed, err = %d\n", -err);
184                 return err;
185         }
186         pe_data.close_me = fds[0];
187         pe_data.stdout = fds[1];
188         pid = run_helper(change_pre_exec, &pe_data, argv, NULL);
189
190         if (pid > 0)    /* Avoid hang as we won't get data in failure case. */
191                 read_output(fds[0], output, output_len);
192
193         os_close_file(fds[0]);
194         os_close_file(fds[1]);
195
196         if (pid > 0)
197                 CATCH_EINTR(err = waitpid(pid, NULL, 0));
198         return pid;
199 }
200
201 static void change(char *dev, char *what, unsigned char *addr,
202                    unsigned char *netmask)
203 {
204         char addr_buf[sizeof("255.255.255.255\0")];
205         char netmask_buf[sizeof("255.255.255.255\0")];
206         char version[sizeof("nnnnn\0")];
207         char *argv[] = { "uml_net", version, what, dev, addr_buf, 
208                          netmask_buf, NULL };
209         char *output;
210         int output_len, pid;
211
212         sprintf(version, "%d", UML_NET_VERSION);
213         sprintf(addr_buf, "%d.%d.%d.%d", addr[0], addr[1], addr[2], addr[3]);
214         sprintf(netmask_buf, "%d.%d.%d.%d", netmask[0], netmask[1], 
215                 netmask[2], netmask[3]);
216
217         output_len = page_size();
218         output = um_kmalloc(output_len);
219         if(output == NULL)
220                 printk("change : failed to allocate output buffer\n");
221
222         pid = change_tramp(argv, output, output_len);
223         if(pid < 0) return;
224
225         if(output != NULL){
226                 printk("%s", output);
227                 kfree(output);
228         }
229 }
230
231 void open_addr(unsigned char *addr, unsigned char *netmask, void *arg)
232 {
233         change(arg, "add", addr, netmask);
234 }
235
236 void close_addr(unsigned char *addr, unsigned char *netmask, void *arg)
237 {
238         change(arg, "del", addr, netmask);
239 }
240
241 char *split_if_spec(char *str, ...)
242 {
243         char **arg, *end;
244         va_list ap;
245
246         va_start(ap, str);
247         while((arg = va_arg(ap, char **)) != NULL){
248                 if(*str == '\0')
249                         return NULL;
250                 end = strchr(str, ',');
251                 if(end != str)
252                         *arg = str;
253                 if(end == NULL)
254                         return NULL;
255                 *end++ = '\0';
256                 str = end;
257         }
258         va_end(ap);
259         return str;
260 }