Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

354 lines (306 sloc) 9.451 kb
/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
* Copyright 2011 Couchbase, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "server.h"
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <ctype.h>
#ifdef linux
#undef ntohs
#undef ntohl
#undef htons
#undef htonl
#endif
static int create_monitor(struct test_server_info *info)
{
struct addrinfo hints, *next, *ai;
int error;
memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_PASSIVE;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
info->sock = -1;
error = getaddrinfo(NULL, "0", &hints, &ai);
if (error != 0) {
if (error != EAI_SYSTEM) {
fprintf(stderr, "getaddrinfo failed: %s\n",
gai_strerror(error));
} else {
perror("getaddrinfo failed:");
}
return 0;
}
for (next = ai; next; next = next->ai_next) {
int flags = 1;
socklen_t len;
if ((info->sock = socket(next->ai_family,
next->ai_socktype,
next->ai_protocol)) == -1) {
continue;
}
setsockopt(info->sock, SOL_SOCKET, SO_REUSEADDR,
(void *)&flags, sizeof(flags));
if (bind(info->sock, next->ai_addr, next->ai_addrlen) == -1) {
close(info->sock);
info->sock = -1;
continue;
} else if (listen(info->sock, 10) == -1) {
close(info->sock);
info->sock = -1;
continue;
}
/* Ok, I've got a working socket :) */
len = sizeof(info->storage);
if (getsockname(info->sock, (struct sockaddr *)&info->storage, &len) == -1) {
close(info->sock);
info->sock = -1;
continue;
}
if (next->ai_addr->sa_family == AF_INET) {
info->port = ntohs((*(struct sockaddr_in *)&info->storage).sin_port);
} else {
info->port = ntohs((*(struct sockaddr_in6 *)&info->storage).sin6_port);
}
}
freeaddrinfo(ai);
return info->sock != -1;
}
static void wait_for_server(const char *port)
{
struct addrinfo hints, *next, *ai;
int sock = -1;
int error;
memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_PASSIVE;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
error = getaddrinfo("localhost", port, &hints, &ai);
if (error != 0) {
if (error != EAI_SYSTEM) {
fprintf(stderr, "getaddrinfo failed: %s\n",
gai_strerror(error));
} else {
perror("getaddrinfo failed:");
}
abort();
}
while (1) {
for (next = ai; next; next = next->ai_next) {
if ((sock = socket(next->ai_family,
next->ai_socktype,
next->ai_protocol)) == -1) {
continue;
}
if (connect(sock, next->ai_addr, next->ai_addrlen) == 0) {
close(sock);
freeaddrinfo(ai);
return;
}
close(sock);
}
usleep(250);
}
}
/**
* Parse server parameters from environment;
* format is host,bucket,username,password
*/
static int parse_server_conf(struct test_server_info *info, const char *param)
{
char *strings[10] = { NULL };
int curix = 0;
char *param_copy = strdup(param);
param = param_copy;
while (*param && curix < 10) {
const char *curfld;
char *curval;
size_t diff;
for (curfld = param; *param && *param != ','; param++);
diff = (param - curfld);
curval = calloc(1, diff + 1);
curval[diff] = '\0';
memcpy(curval, curfld, diff);
strings[curix++] = curval;
if (*param == ',') {
param++;
}
}
info->http = strings[0];
info->bucket = strings[1];
info->username = strings[2];
info->password = strings[3];
free(param_copy);
if (!info->http) {
fprintf(stderr, "Must have node entry point for real cluster test\n");
return 0;
}
return 1;
}
static int start_mock_server(struct test_server_info *info, char **cmdline)
{
char wrapper[1024];
const char *srcdir = getenv("srcdir");
if (srcdir == NULL) {
srcdir = ".";
}
snprintf(wrapper, sizeof(wrapper), "%s/tests/start_mock.sh", srcdir);
if (access(wrapper, X_OK) == -1) {
fprintf(stderr, "Failed to locate \"./tests/start_mock.sh\": %s\n",
strerror(errno));
return 0;
}
if (!create_monitor(info)) {
return 0;
}
info->pid = fork();
assert(info->pid != -1);
if (info->pid == 0) {
/* Child */
char monitor[1024];
char *argv[1024];
int arg = 0;
argv[arg++] = (char *)wrapper;
sprintf(monitor, "--harakiri-monitor=localhost:%d", info->port);
argv[arg++] = monitor;
if (cmdline != NULL) {
int ii = 0;
while (cmdline[ii] != NULL && arg < 1022) {
argv[arg++] = cmdline[ii++];
}
}
argv[arg++] = NULL;
execv(argv[0], argv);
abort();
} else {
char buffer[1024];
ssize_t offset;
ssize_t nr;
int ii;
/* wait until the server connects */
for (ii = 0; ii < 10; ii++) {
info->client = accept(info->sock, NULL, NULL);
if (info->client == -1) {
/* running this in gdb on OS X, I got an EINTR a few times */
if (errno == EINTR) {
fprintf(stderr, "start_mock_server: Sleeping 1 second on EINTR\n");
sleep(1);
} else {
perror("start_mock_server");
abort();
}
} else {
break;
}
}
assert(info->client != -1);
/* Get the port number of the http server */
offset = snprintf(buffer, sizeof(buffer), "localhost:");
nr = recv(info->client, buffer + offset,
sizeof(buffer) - (size_t)offset - 1, 0);
assert(nr > 0);
buffer[nr + offset] = '\0';
info->http = strdup(buffer);
wait_for_server(buffer + offset);
}
sleep(1); /* give it a bit time to initialize itself */
return 1;
}
const void *start_test_server(char **cmdline)
{
const char *clconf = getenv(LCB_TEST_REALCLUSTER_ENV);
struct test_server_info *info = calloc(1, sizeof(*info));
int server_ok = 0;
if (info == NULL) {
return NULL;
}
if (clconf) {
server_ok = parse_server_conf(info, clconf);
info->is_mock = 0;
} else {
server_ok = start_mock_server(info, cmdline);
info->is_mock = 1;
}
if (!server_ok) {
fprintf(stderr, "Couldn't setup server!\n");
abort();
}
return info;
}
void failover_node(const void *handle, int idx, const char *bucket)
{
struct test_server_info *info = (void *)handle;
char buffer[1024];
int nb = snprintf(buffer, 1024, "failover,%d,%s\n", idx, bucket ? bucket : "default");
ssize_t nw = send(info->client, buffer, (size_t)nb, 0);
assert(nw == nb);
}
void respawn_node(const void *handle, int idx, const char *bucket)
{
struct test_server_info *info = (void *)handle;
char buffer[1024];
int nb = snprintf(buffer, 1024, "respawn,%d,%s\n", idx, bucket ? bucket : "default");
ssize_t nw = send(info->client, buffer, (size_t)nb, 0);
assert(nw == nb);
}
void shutdown_mock_server(const void *handle)
{
struct test_server_info *info = (void *)handle;
free(info->http);
free(info->bucket);
free(info->username);
free(info->password);
if (info->is_mock) {
close(info->client);
close(info->sock);
kill(info->pid, SIGTERM);
}
free((void *)handle);
}
const char *get_mock_http_server(const void *handle)
{
struct test_server_info *info = (void *)handle;
return info->http;
}
void get_mock_std_creds(const void *handle, const char **userp, const char **passp)
{
const struct test_server_info *info = handle;
if (info->is_mock) {
*userp = "Administrator";
*passp = "password";
} else {
*userp = info->username;
*passp = info->password;
}
}
int is_using_real_cluster(void)
{
return getenv(LCB_TEST_REALCLUSTER_ENV) != NULL;
}
Jump to Line
Something went wrong with that request. Please try again.