blob: 0ca9139030a2a83879075d37e073d3de35de974b [file]
/*
* Copyright (c) 2012 The Native Client Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include "native_client/src/public/imc_syscalls.h"
#include "native_client/src/shared/srpc/nacl_srpc.h"
#define BOUND_SOCKET 3
/*
* NativeClient server side of a test for passing socket addresses. The
* server side steps are:
* 1) The JavaScript bridge invokes the start_server method.
* 2) StartServer creates a bound socket and socket address pair.
* 3) StartServer creates a server pthread listening on the bound socket.
* 4) StartServer returns the socket address to the JavaScript bridge.
* 5) The JavaScript bridge starts a client, either in itself or another
* NativeClient module.
* 6) The server accepts a connection, setting up an SRPC server on the
* resulting connected socket.
* 7) The server handles an RPC to get a message.
* 8) The JavaScript bridge invokes the report message to find out if there
* were errors.
*/
/* The count of errors seen during the test. Returned by the report method. */
int errors_seen = 0;
/*
* ServerThread awaits connection on the specified descriptor. Once connected,
* ServerThread runs an SRPC server that has one method.
*/
void* ServerThread(void* desc);
/* StartServer creates a new SRPC server thread each time it is invoked. */
void StartServer(NaClSrpcRpc *rpc,
NaClSrpcArg **in_args,
NaClSrpcArg **out_args,
NaClSrpcClosure *done) {
int pair[2];
pthread_t server;
int rv;
/* Create a pair (bound socket, socket address). */
printf("StartServer: creating bound socket\n");
rv = imc_makeboundsock(pair);
if (rv == 0) {
printf("StartServer: bound socket %d, address %d\n", pair[0], pair[1]);
} else {
printf("StartServer: bound socket creation FAILED, errno %d\n", errno);
++errors_seen;
}
/* Pass the socket address back to the caller. */
out_args[0]->u.hval = pair[1];
/* Start the server, passing ownership of the bound socket. */
printf("StartServer: creating server...\n");
rv = pthread_create(&server, NULL, ServerThread, (void*) pair[0]);
if (rv == 0) {
printf("StartServer: server created\n");
} else {
printf("StartServer: server creation FAILED, errno %d\n", errno);
++errors_seen;
}
/* Return success. */
printf("START RPC FINISHED\n");
rpc->result = NACL_SRPC_RESULT_OK;
done->Run(done);
}
/* GetMsg simply returns a string in a character array. */
static void GetMsg(NaClSrpcRpc *rpc,
NaClSrpcArg **in_args,
NaClSrpcArg **out_args,
NaClSrpcClosure *done) {
static char message[] = "Quidquid id est, timeo Danaos et dona ferentes";
printf("ServerThread: GetMsg\n");
if (out_args[0]->u.count >= strlen(message)) {
strncpy(out_args[0]->arrays.carr, message, strlen(message) + 1);
rpc->result = NACL_SRPC_RESULT_OK;
} else {
printf("GetMsg: u.count %u is too small %u\n",
(unsigned) out_args[0]->u.count,
(unsigned) strlen(message));
++errors_seen;
rpc->result = NACL_SRPC_RESULT_APP_ERROR;
}
done->Run(done);
}
/* Shutdown stops the RPC service. */
static void Shutdown(NaClSrpcRpc *rpc,
NaClSrpcArg **in_args,
NaClSrpcArg **out_args,
NaClSrpcClosure *done) {
printf("ServerThread: Shutdown\n");
rpc->result = NACL_SRPC_RESULT_BREAK;
done->Run(done);
}
void* ServerThread(void* desc) {
int connected_desc;
printf("ServerThread: waiting on %d to accept connections...\n",
(int) desc);
fflush(stdout);
/* Wait for connections from the client. */
connected_desc = imc_accept((int) desc);
if (connected_desc >= 0) {
static struct NaClSrpcHandlerDesc methods[] = {
{ "getmsg::C", GetMsg },
{ "shutdown::", Shutdown },
{ NULL, NULL }
};
/* Export the server on the connected socket descriptor. */
if (!NaClSrpcServerLoop(connected_desc, methods, NULL)) {
printf("SRPC server loop failed.\n");
++errors_seen;
}
printf("ServerThread: shutting down\n");
/* Close the connected socket */
if (0 != close(connected_desc)) {
printf("ServerThread: connected socket close failed.\n");
++errors_seen;
}
} else {
printf("ServerThread: connection FAILED, errno %d\n", errno);
++errors_seen;
}
/* Close the bound socket */
if (0 != close((int) desc)) {
printf("ServerThread: bound socket close failed.\n");
++errors_seen;
}
printf("THREAD EXIT\n");
return 0;
}
/*
* TestSharedMemory tests the passing of shared memory regions.
* It expects to receive a handle and a string.
*/
void TestSharedMemory(NaClSrpcRpc *rpc,
NaClSrpcArg **in_args,
NaClSrpcArg **out_args,
NaClSrpcClosure *done) {
int desc = in_args[0]->u.hval;
char* compare_string = in_args[1]->arrays.str;
char* map_addr;
struct stat st;
printf("TestSharedMemory(%d, %s)\n", desc, compare_string);
if (fstat(desc, &st)) {
printf("TestSharedMemory: fstat failed\n");
++errors_seen;
} else {
size_t length = (size_t) st.st_size;
printf("TestSharedMemory: fstat returned 0x%08x\n", length);
/* Map the shared memory region into the NaCl module's address space. */
map_addr = (char*) mmap(NULL,
length,
PROT_READ | PROT_WRITE,
MAP_SHARED,
desc,
0);
if (MAP_FAILED == map_addr) {
printf("TestSharedMemory: map failed\n");
++errors_seen;
} else {
printf("TestSharedMemory: mapped memory string: %12s\n", map_addr);
if (strncmp(map_addr, compare_string, strlen(compare_string))) {
printf("TestSharedMemory: strncmp failed\n");
++errors_seen;
} else {
const char* reply = "Quod erat demonstrandum";
strncpy(map_addr + strlen(compare_string), reply, strlen(reply));
/* Unmap the memory region. */
if (munmap((void*) map_addr, length)) {
printf("TestSharedMemory: munmap failed\n");
++errors_seen;
} else {
/* Close the passed-in descriptor. */
if (close(desc)) {
printf("TestSharedMemory: close failed\n");
++errors_seen;
}
}
}
}
}
/* Report the number of errors seen back as the result. */
out_args[0]->u.ival = errors_seen;
printf("TestSharedMemory: returning errors %d\n", errors_seen);
rpc->result = NACL_SRPC_RESULT_OK;
done->Run(done);
}
/* Report reports the number of errors seen during the tests. */
void Report(NaClSrpcRpc *rpc,
NaClSrpcArg **in_args,
NaClSrpcArg **out_args,
NaClSrpcClosure *done) {
out_args[0]->u.ival = errors_seen;
rpc->result = NACL_SRPC_RESULT_OK;
done->Run(done);
}
const struct NaClSrpcHandlerDesc srpc_methods[] = {
{ "start_server::h", StartServer },
{ "test_shared_memory:hs:i", TestSharedMemory },
{ "report::i", Report },
{ NULL, NULL },
};
int main(void) {
assert(NaClSrpcModuleInit());
/*
* We need to be able to handle multiple connections because
* srpc_sockaddr.html tests this by doing
* __defaultSocketAddress().connect(). So, rather than using
* NaClSrpcAcceptClientConnection(), we handle multiple connections
* explicitly.
*/
while (1) {
assert(NaClSrpcAcceptClientOnThread(srpc_methods));
}
NaClSrpcModuleFini();
}