Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
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.