1 /*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 /* This program is used to test the QEMUD fast pipes.
18 * See external/qemu/docs/ANDROID-QEMUD-PIPES.TXT for details.
19 *
20 * The program acts as a simple TCP server that accepts data and sends
21 * them back to the client as is.
22 */
23 #include <sys/socket.h>
24 #include <netinet/in.h>
25 #include <sys/un.h>
26 #include <unistd.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <errno.h>
31
32 /* Default port number */
33 #define DEFAULT_PORT 8012
34 #define DEFAULT_PATH "/tmp/libqemu-socket"
35
36 /* Try to execute x, looping around EINTR errors. */
37 #undef TEMP_FAILURE_RETRY
38 #define TEMP_FAILURE_RETRY(exp) ({ \
39 typeof (exp) _rc; \
40 do { \
41 _rc = (exp); \
42 } while (_rc == -1 && errno == EINTR); \
43 _rc; })
44
45 #define TFR TEMP_FAILURE_RETRY
46
47 /* Close a socket, preserving the value of errno */
48 static void
socket_close(int sock)49 socket_close(int sock)
50 {
51 int old_errno = errno;
52 close(sock);
53 errno = old_errno;
54 }
55
56 /* Create a server socket bound to a loopback port */
57 static int
socket_loopback_server(int port,int type)58 socket_loopback_server( int port, int type )
59 {
60 struct sockaddr_in addr;
61
62 int sock = socket(AF_INET, type, 0);
63 if (sock < 0) {
64 return -1;
65 }
66
67 memset(&addr, 0, sizeof(addr));
68 addr.sin_family = AF_INET;
69 addr.sin_port = htons(port);
70 addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
71
72 int n = 1;
73 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n));
74
75 if (TFR(bind(sock, (struct sockaddr*)&addr, sizeof(addr))) < 0) {
76 socket_close(sock);
77 return -1;
78 }
79
80 if (type == SOCK_STREAM) {
81 if (TFR(listen(sock, 4)) < 0) {
82 socket_close(sock);
83 return -1;
84 }
85 }
86
87 return sock;
88 }
89
90 static int
socket_unix_server(const char * path,int type)91 socket_unix_server( const char* path, int type )
92 {
93 struct sockaddr_un addr;
94
95 int sock = socket(AF_UNIX, type, 0);
96 if (sock < 0) {
97 return -1;
98 }
99
100 memset(&addr, 0, sizeof(addr));
101 addr.sun_family = AF_UNIX;
102 snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", path);
103
104 unlink(addr.sun_path);
105
106 printf("Unix path: '%s'\n", addr.sun_path);
107
108 int n = 1;
109 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n));
110
111 if (TFR(bind(sock, (struct sockaddr*)&addr, sizeof(addr))) < 0) {
112 socket_close(sock);
113 return -1;
114 }
115
116 if (type == SOCK_STREAM) {
117 if (TFR(listen(sock, 4)) < 0) {
118 socket_close(sock);
119 return -1;
120 }
121 }
122
123 return sock;
124 }
125
126 char* progname;
127
usage(int code)128 static void usage(int code)
129 {
130 printf("Usage: %s [options]\n\n", progname);
131 printf(
132 "Valid options are:\n\n"
133 " -? -h --help Print this message\n"
134 " -unix <path> Use unix server socket\n"
135 " -tcp <port> Use local tcp port (default %d)\n"
136 "\n", DEFAULT_PORT
137 );
138 exit(code);
139 }
140
141 /* Main program */
main(int argc,char ** argv)142 int main(int argc, char** argv)
143 {
144 int sock, client;
145 int port = DEFAULT_PORT;
146 const char* path = NULL;
147 const char* tcpPort = NULL;
148
149 /* Extract program name */
150 {
151 char* p = strrchr(argv[0], '/');
152 if (p == NULL)
153 progname = argv[0];
154 else
155 progname = p+1;
156 }
157
158 /* Parse options */
159 while (argc > 1 && argv[1][0] == '-') {
160 char* arg = argv[1];
161 if (!strcmp(arg, "-?") || !strcmp(arg, "-h") || !strcmp(arg, "--help")) {
162 usage(0);
163 } else if (!strcmp(arg, "-unix")) {
164 if (argc < 3) {
165 fprintf(stderr, "-unix option needs an argument! See --help for details.\n");
166 exit(1);
167 }
168 argc--;
169 argv++;
170 path = argv[1];
171 } else if (!strcmp(arg, "-tcp")) {
172 if (argc < 3) {
173 fprintf(stderr, "-tcp option needs an argument! See --help for details.\n");
174 exit(1);
175 }
176 argc--;
177 argv++;
178 tcpPort = argv[1];
179 } else {
180 fprintf(stderr, "UNKNOWN OPTION: %s\n\n", arg);
181 usage(1);
182 }
183 argc--;
184 argv++;
185 }
186
187 if (path != NULL) {
188 printf("Starting pipe test server on unix path: %s\n", path);
189 sock = socket_unix_server( path, SOCK_STREAM );
190 } else {
191 printf("Starting pipe test server on local port %d\n", port);
192 sock = socket_loopback_server( port, SOCK_STREAM );
193 }
194 if (sock < 0) {
195 fprintf(stderr, "Could not start server: %s\n", strerror(errno));
196 return 1;
197 }
198 printf("Server ready!\n");
199
200 RESTART:
201 client = TFR(accept(sock, NULL, NULL));
202 if (client < 0) {
203 fprintf(stderr, "Server error: %s\n", strerror(errno));
204 return 2;
205 }
206 printf("Client connected!\n");
207
208 /* Now, accept any incoming data, and send it back */
209 for (;;) {
210 char buff[32768], *p;
211 int ret, count;
212
213 ret = TFR(read(client, buff, sizeof(buff)));
214 if (ret < 0) {
215 fprintf(stderr, "Client read error: %s\n", strerror(errno));
216 socket_close(client);
217 return 3;
218 }
219 if (ret == 0) {
220 break;
221 }
222 count = ret;
223 p = buff;
224 //printf(" received: %d bytes\n", count);
225
226 while (count > 0) {
227 ret = TFR(write(client, p, count));
228 if (ret < 0) {
229 fprintf(stderr, "Client write error: %s\n", strerror(errno));
230 socket_close(client);
231 return 4;
232 }
233 //printf(" sent: %d bytes\n", ret);
234
235 p += ret;
236 count -= ret;
237 }
238 }
239 printf("Client closed connection\n");
240 socket_close(client);
241 goto RESTART;
242
243 return 0;
244 }
245