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 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 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 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 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 */ 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