1/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2/* 3 * Copyright 2011-2020 Couchbase, Inc. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17#include "config.h" 18#include "check_config.h" 19#include "server.h" 20#include <stdio.h> 21#include <stdlib.h> 22#include <errno.h> 23#include <ctype.h> 24 25#ifndef _WIN32 26#define DIRSEP "/" 27#include <pthread.h> 28#include <sys/wait.h> 29#include <arpa/inet.h> 30#include <netinet/tcp.h> 31#include <netinet/in.h> 32#include <signal.h> 33#ifdef linux 34#undef ntohs 35#undef ntohl 36#undef htons 37#undef htonl 38#endif 39#else 40#define DIRSEP "\\" 41#include <io.h> /* for access() */ 42#endif /* ! _WIN32 */ 43 44static int create_monitor(struct test_server_info *info) 45{ 46 struct addrinfo hints, *next, *ai; 47 int error; 48 49 memset(&hints, 0, sizeof(hints)); 50 hints.ai_flags = AI_PASSIVE; 51 hints.ai_family = AF_UNSPEC; 52 hints.ai_socktype = SOCK_STREAM; 53 54 info->sock = -1; 55 error = getaddrinfo(NULL, "0", &hints, &ai); 56 if (error != 0) { 57#ifdef _WIN32 58 if (0) { 59#else 60 if (error != EAI_SYSTEM) { 61#endif 62 fprintf(stderr, "getaddrinfo failed: %s\n", gai_strerror(error)); 63 } else { 64 perror("getaddrinfo failed:"); 65 } 66 return 0; 67 } 68 69 for (next = ai; next; next = next->ai_next) { 70 int flags = 1; 71 socklen_t len; 72 73 if ((info->sock = socket(next->ai_family, next->ai_socktype, next->ai_protocol)) == -1) { 74 continue; 75 } 76 77 setsockopt(info->sock, SOL_SOCKET, SO_REUSEADDR, (void *)&flags, sizeof(flags)); 78 79 if (bind(info->sock, next->ai_addr, next->ai_addrlen) == -1) { 80 closesocket(info->sock); 81 info->sock = -1; 82 continue; 83 } else if (listen(info->sock, 10) == -1) { 84 closesocket(info->sock); 85 info->sock = -1; 86 continue; 87 } 88 89 /* Ok, I've got a working socket :) */ 90 len = sizeof(info->storage); 91 if (getsockname(info->sock, (struct sockaddr *)&info->storage, &len) == -1) { 92 closesocket(info->sock); 93 info->sock = -1; 94 continue; 95 } 96 if (next->ai_addr->sa_family == AF_INET) { 97 info->port = ntohs((*(struct sockaddr_in *)&info->storage).sin_port); 98 } else { 99 info->port = ntohs((*(struct sockaddr_in6 *)&info->storage).sin6_port); 100 } 101 } 102 103 freeaddrinfo(ai); 104 return info->sock != -1; 105} 106 107static void wait_for_server(const char *port) 108{ 109 struct addrinfo hints, *next, *ai; 110 int sock = -1; 111 int error; 112 113 memset(&hints, 0, sizeof(hints)); 114 hints.ai_flags = AI_PASSIVE; 115 hints.ai_family = AF_UNSPEC; 116 hints.ai_socktype = SOCK_STREAM; 117 118 error = getaddrinfo("localhost", port, &hints, &ai); 119 if (error != 0) { 120#ifndef _WIN32 121 if (error != EAI_SYSTEM) { 122#else 123 if (0) { 124#endif 125 fprintf(stderr, "getaddrinfo failed: %s\n", gai_strerror(error)); 126 } else { 127 perror("getaddrinfo failed:"); 128 } 129 abort(); 130 } 131 132 while (1) { 133 for (next = ai; next; next = next->ai_next) { 134 if ((sock = socket(next->ai_family, next->ai_socktype, next->ai_protocol)) == -1) { 135 continue; 136 } 137 138 if (connect(sock, next->ai_addr, next->ai_addrlen) == 0) { 139 closesocket(sock); 140 freeaddrinfo(ai); 141 return; 142 } 143 144 closesocket(sock); 145 } 146 usleep(250); 147 } 148} 149 150/** 151 * Parse server parameters from environment; 152 * format is host,bucket,username,password 153 */ 154static int parse_server_conf(struct test_server_info *info, const char *param) 155{ 156 char *strings[10] = {NULL}; 157 int curix = 0; 158 char *param_copy = strdup(param); 159 param = param_copy; 160 161 while (*param && curix < 10) { 162 const char *curfld; 163 char *curval; 164 size_t diff; 165 166 for (curfld = param; *param && *param != ','; param++) 167 ; 168 diff = (param - curfld); 169 curval = calloc(1, diff + 1); 170 curval[diff] = '\0'; 171 memcpy(curval, curfld, diff); 172 strings[curix++] = curval; 173 if (*param == ',') { 174 param++; 175 } 176 } 177 178 info->http = strings[0]; 179 info->bucket = strings[1]; 180 info->username = strings[2]; 181 info->password = strings[3]; 182 183 free(param_copy); 184 185 if (!info->http) { 186 fprintf(stderr, "Must have node entry point for real cluster test\n"); 187 return 0; 188 } 189 return 1; 190} 191 192static int start_mock_process(struct test_server_info *info, char **argv) 193{ 194 char argbuf[4096] = {0}; 195 char **arg; 196 for (arg = argv; *arg; arg++) { 197 strcat(argbuf, *arg); 198 strcat(argbuf, " "); 199 } 200 201 memset(&info->process, 0, sizeof(info->process)); 202 info->process.name = argbuf; 203 204 return create_process(&info->process); 205} 206 207static void kill_mock_process(struct test_server_info *info) 208{ 209 kill_process(&info->process, 1); 210 wait_process(&info->process, 1); 211 cleanup_process(&info->process); 212} 213 214#ifndef _WIN32 215#define WRAPPER_BASE "start_mock.sh" 216#else 217#define WRAPPER_BASE "start_mock.bat" 218#endif 219 220static void negotiate_mock_connection(struct test_server_info *info) 221{ 222 char buffer[1024]; 223 lcb_ssize_t offset; 224 lcb_ssize_t nr; 225 int ii; 226 227 /* wait until the server connects */ 228 for (ii = 0; ii < 10; ii++) { 229 info->client = accept(info->sock, NULL, NULL); 230 if (info->client == -1) { 231 /* running this in gdb on OS X, I got an EINTR a few times */ 232 if (errno == EINTR) { 233 fprintf(stderr, "start_mock_server: Sleeping 1 second on EINTR\n"); 234 sleep(1); 235 } else { 236 perror("start_mock_server"); 237 abort(); 238 } 239 } else { 240 break; 241 } 242 } 243 244 lcb_assert(info->client != -1); 245 /* Get the port number of the http server */ 246 offset = snprintf(buffer, sizeof(buffer), "localhost:"); 247 nr = recv(info->client, buffer + offset, sizeof(buffer) - (size_t)offset - 1, 0); 248 lcb_assert(nr > 0); 249 buffer[nr + offset] = '\0'; 250 info->http = strdup(buffer); 251 wait_for_server(buffer + offset); 252} 253 254static int start_mock_server(struct test_server_info *info, char **cmdline) 255{ 256 257 char wrapper[1024]; 258 char monitor[1024]; 259 char *argv[1024]; 260#ifdef _WIN32 261 int access_mode = 00; 262#else 263 int access_mode = X_OK; 264#endif 265 const char *srcdir = getenv("srcdir"); 266 267 if (srcdir == NULL) { 268 srcdir = TEST_SRC_DIR; 269 } 270 271 snprintf(wrapper, sizeof(wrapper), "%s" DIRSEP "tests" DIRSEP WRAPPER_BASE, srcdir); 272 273 if (access(wrapper, access_mode) == -1) { 274 fprintf(stderr, "Failed to locate \"%s\": %s\n", wrapper, strerror(errno)); 275 return 0; 276 } 277 278 if (!create_monitor(info)) { 279 return 0; 280 } 281 282 { 283 int arg = 0; 284 argv[arg++] = (char *)wrapper; 285 sprintf(monitor, "--harakiri-monitor=localhost:%d", info->port); 286 argv[arg++] = monitor; 287 288 if (cmdline != NULL) { 289 int ii = 0; 290 while (cmdline[ii] != NULL && arg < 1022) { 291 argv[arg++] = cmdline[ii++]; 292 } 293 } 294 argv[arg++] = NULL; 295 } 296 297 start_mock_process(info, argv); 298 negotiate_mock_connection(info); 299 return 1; 300} 301 302const void *start_test_server(char **cmdline) 303{ 304 const char *clconf = getenv(LCB_TEST_REALCLUSTER_ENV); 305 int server_ok = 0; 306 struct test_server_info *info = calloc(1, sizeof(*info)); 307 308#ifdef _WIN32 309 /** Winsock boilerplate */ 310 { 311 WSADATA wsaData; 312 if (WSAStartup(MAKEWORD(2, 0), &wsaData) != 0) { 313 fprintf(stderr, "WSAStartup failed. Abort\n"); 314 abort(); 315 } 316 } 317#endif 318 319 if (info == NULL) { 320 return NULL; 321 } 322 323 if (clconf) { 324 server_ok = parse_server_conf(info, clconf); 325 info->is_mock = 0; 326 } else { 327 server_ok = start_mock_server(info, cmdline); 328 info->is_mock = 1; 329 } 330 331 if (!server_ok) { 332 fprintf(stderr, "Couldn't setup server!\n"); 333 abort(); 334 } 335 336 return info; 337} 338 339void shutdown_mock_server(const void *handle) 340{ 341 struct test_server_info *info = (void *)handle; 342 if (info != NULL) { 343 free(info->http); 344 free(info->bucket); 345 free(info->username); 346 free(info->password); 347 if (info->is_mock) { 348 closesocket(info->client); 349 closesocket(info->sock); 350 kill_mock_process(info); 351 } 352 free((void *)handle); 353 } 354} 355 356const char *get_mock_http_server(const void *handle) 357{ 358 struct test_server_info *info = (void *)handle; 359 return info->http; 360} 361 362void get_mock_std_creds(const void *handle, const char **userp, const char **passp) 363{ 364 const struct test_server_info *info = handle; 365 if (info->is_mock) { 366 *userp = NULL; 367 *passp = NULL; 368 } else { 369 *userp = info->username; 370 *passp = info->password; 371 } 372} 373 374int is_using_real_cluster(void) 375{ 376 return getenv(LCB_TEST_REALCLUSTER_ENV) != NULL; 377} 378