# --------------------------------------------------------------- # # -- Copyright (c) 1994, 1995 Computer Sciences Department, -- # # -- University of Wisconsin-Madison, subject to the terms -- # # -- and conditions given in the file COPYRIGHT. All Rights -- # # -- Reserved. -- # # --------------------------------------------------------------- # # $Header: /p/shore/shore_cvs/src/examples/vas/grid/Makefile.template,v 1.17 1996/07/30 19:40:21 kupsch Exp $ # # Makefile for vas/grid program # # See "NOTE" below re: running on Solaris. # also see the note re: linking with DEBUG library # Modify the following line to indicated where gcc is installed GCC = gcc # Modify the following line as appropriate to point to the place where # Shore is installed INSTALL_DIR = /p/shore/installed/NEW/debug INCLUDE = -I$(INSTALL_DIR)/include LIBSM = $(INSTALL_DIR)/lib/libsm.a # These are set to use the modified RPC shipped with Shore RPCGEN = $(INSTALL_DIR)/bin/rpcgen LIBCOMMON = $(INSTALL_DIR)/lib/librpclib.a $(INSTALL_DIR)/lib/libshorecommon.a SERVER_SRCS = grid.C rpc_thread.C server.C command_server.C server_stubs.C CLIENT_SRCS = client.C command_client.C COMMON_SRCS = options.C command.C grid_basics.C EXECS = server client SERVER_OBJS = $(SERVER_SRCS:.C=.o) CLIENT_OBJS = $(CLIENT_SRCS:.C=.o) COMMON_OBJS = $(COMMON_SRCS:.C=.o) OBJS = $(SERVER_OBJS) $(CLIENT_OBJS) $(COMMON_OBJS) CXX = $(GCC) -x c++ CC = $(GCC) LD = $(GCC) LDFLAGS = -lg++ -lstdc++ # NOTE: if running on a Solaris machine, add this: LDFLAGS += -lnsl -lsocket # # C/C++ Flags. # # Note: if you are linking with a debugging version of the Storage # manager libraries, be sure to define DEBUG with -DDEBUG # CCFLAGS = -g -O $(INCLUDE) -Wall CXXFLAGS = $(CCFLAGS) -fno-implicit-templates all: $(EXECS) .SUFFIXES: .C .o .C.o: $(CXX) $(CXXFLAGS) -c $< .c.o: $(CC) $(CCFLAGS) -c $< # NB: THIS IS IMPORTANT: we include "ShoreConfig.h" because # we *NEED* the system-dependent definition of a jmp_buf # in order to see that the thread data structures that # that we build here are consistent with those in the library. # If we don't get the right #defines for the configuration, we # run the risk of building .o files here with the wrong idea # about the size of sthread_t (the root of the class hierarchy # for our threads). RPC_FILES = msg.h msg_clnt.c msg_svc.c msg_xdr.c $(RPC_FILES): msg.x cp $(INSTALL_DIR)/include/ShoreConfig.h . $(RPCGEN) msg.x rm -f msg_svc.c $(RPCGEN) -m -o msg_svc.c msg.x server: $(SERVER_OBJS) $(COMMON_OBJS) msg_xdr.o msg_svc.o $(LIBSM) $(LIBCOMMON) $(LD) -o $@ msg_svc.o $(SERVER_OBJS) $(COMMON_OBJS) msg_xdr.o \ $(LIBSM) $(LIBCOMMON) -lm $(LDFLAGS) client: $(CLIENT_OBJS) $(COMMON_OBJS) $(LIBCOMMON) msg_clnt.o msg_xdr.o $(LD) -o $@ $(CLIENT_OBJS) $(COMMON_OBJS) msg_clnt.o msg_xdr.o \ $(LIBCOMMON) $(LDFLAGS) clean: $(RM) $(EXECS) $(OBJS) $(RPC_FILES) a.out core tags #dependencies $(SERVER_OBJS) $(CLIENT_OBJS) $(COMMON_OBJS): msg.h grid_basics.h grid.h msg.h: grid_basics.h msg_svc.o msg_clnt.o msg_xdr.o: msg.h grid_basics.h client.o: command.h command_client.h rpc_thread.o: rpc_thread.h command.h command_server.h server.o: rpc_thread.h command.o: command.h command_client.o: command.h command_client.h command_server.o server_stubs.o: command.h command_server.h rpc_thread.h
/* --------------------------------------------------------------- */
/* -- Copyright (c) 1994, 1995 Computer Sciences Department, -- */
/* -- University of Wisconsin-Madison, subject to the terms -- */
/* -- and conditions given in the file COPYRIGHT. All Rights -- */
/* -- Reserved. -- */
/* --------------------------------------------------------------- */
%#ifndef GRID_BASICS_H
#include "grid_basics.h"
%#endif /* !GRID_BASICS_H */
/* const MAXNAMELEN = MAX_NAME_LEN;*/
typedef char name_type_t[MAX_NAME_LEN];
typedef char error_msg_t[MAX_ERR_MSG_LEN];
struct error_reply {
error_msg_t error_msg;
};
struct print_grid_reply {
grid_display_t display;
error_msg_t error_msg;
};
struct add_item_arg {
name_type_t name;
int x;
int y;
};
struct remove_item_arg {
name_type_t name;
};
struct location_arg {
name_type_t name;
};
struct location_reply {
int x;
int y;
error_msg_t error_msg;
};
struct spatial_arg {
int x_low;
int y_low;
int x_hi;
int y_hi;
};
struct spatial_reply {
spatial_result_t result;
error_msg_t error_msg;
};
program GRID {
version GRIDVERS {
void ping_rpc(void) = 0;
error_reply commit_transaction_rpc(void) = 101;
error_reply abort_transaction_rpc(void) = 102;
error_reply clear_grid_rpc(void) = 103;
print_grid_reply print_grid_rpc(void) = 104;
error_reply add_item_rpc(add_item_arg) = 105;
error_reply remove_item_rpc(remove_item_arg) = 106;
error_reply move_item_rpc(add_item_arg) = 107;
location_reply location_of_rpc(location_arg) = 108;
spatial_reply spatial_rpc(spatial_arg) = 109;
} = 1;
} = 0x20000100;
#ifdef RPC_HDR
%#define MSG_H
%#ifdef RPC_SVC
%#ifdef __cplusplus
%/*
% * Maximum size of all replys
% * Used to create a sufficiently large a reply buffer in a thread.
% */
% const size_t thread_reply_buf_size = MAX(sizeof(error_reply),
% MAX(sizeof(print_grid_reply),
% MAX(sizeof(location_reply),
% sizeof(spatial_reply))));
%/* server dispatch function */
%extern "C" void grid_1(struct svc_req*, register SVCXPRT*);
%/* Server side of RPCs */
%extern "C" void* ping_rpc_1(void*, svc_req*);
%extern "C" error_reply* commit_transaction_rpc_1(void*, svc_req*);
%extern "C" error_reply* abort_transaction_rpc_1(void*, svc_req*);
%extern "C" error_reply* clear_grid_rpc_1(void*, svc_req*);
%extern "C" print_grid_reply* print_grid_rpc_1(void*, svc_req*);
%extern "C" error_reply* add_item_rpc_1(add_item_arg*, svc_req*);
%extern "C" error_reply* remove_item_rpc_1(remove_item_arg*, svc_req*);
%extern "C" error_reply* move_item_rpc_1(add_item_arg*, svc_req*);
%extern "C" location_reply* location_of_rpc_1(location_arg*, svc_req*);
%extern "C" spatial_reply* spatial_rpc_1(spatial_arg*, svc_req*);
%#endif /*__cplusplus*/
%#endif /*RPC_SVC*/
%#ifdef RPC_CLNT
%#ifdef __cplusplus
%extern "C" void* ping_rpc_1(void*, CLIENT*);
%extern "C" error_reply* commit_transaction_rpc_1(void*, CLIENT*);
%extern "C" error_reply* abort_transaction_rpc_1(void*, CLIENT*);
%extern "C" error_reply* clear_grid_rpc_1(void*, CLIENT*);
%extern "C" print_grid_reply* print_grid_rpc_1(void*, CLIENT*);
%extern "C" error_reply* add_item_rpc_1(add_item_arg*, CLIENT*);
%extern "C" error_reply* remove_item_rpc_1(remove_item_arg*, CLIENT*);
%extern "C" error_reply* move_item_rpc_1(add_item_arg*, CLIENT*);
%extern "C" location_reply* location_of_rpc_1(location_arg*, CLIENT*);
%extern "C" spatial_reply* spatial_rpc_1(spatial_arg*, CLIENT*);
%#endif /*__cplusplus*/
%#endif /*RPC_CLNT*/
#endif /*RPC_HDR*/
/* --------------------------------------------------------------- */
/* -- Copyright (c) 1994, 1995 Computer Sciences Department, -- */
/* -- University of Wisconsin-Madison, subject to the terms -- */
/* -- and conditions given in the file COPYRIGHT. All Rights -- */
/* -- Reserved. -- */
/* --------------------------------------------------------------- */
#ifndef GRID_BASICS_H
#define GRID_BASICS_H
#include "ShoreConfig.h"
/*
* This defines a bunch of commonly used constants for the grid
* program.
*
* Note: #define is since this file must be run through rpcgen.
*/
/*
* Maximun length of an item name
*/
#define MAX_NAME_LEN 21
/*
* Max error message length
*/
#define MAX_ERR_MSG_LEN 80
/*
* Maximum size of grid
*/
#define MAX_GRID_X 40
#define MAX_GRID_Y 15
/*
* Maximum # of items returns from a spatial query
*/
#define MAX_SPATIAL_RESULT 10
/*
* Items on the grid
* Note: #ifdef __cplusplus is to avoid sending c++ code
* through rpcgen (since it can't handle it)
*/
struct item_t {
#ifdef __cplusplus
item_t();
item_t(const char* _name, int _x, int _y);
void init(const char* _name, int _x, int _y);
#endif
/* location on grid */
int x;
int y;
/* name of the item */
char name[MAX_NAME_LEN];
};
/*
*/
/*
* Query Result Structures
*/
typedef char grid_display_row_t[MAX_GRID_X];
struct grid_display_t {
grid_display_row_t rows[MAX_GRID_Y];
};
/* results for spatial queries */
struct spatial_result_t {
int found_cnt; /* number of items found */
item_t items[MAX_SPATIAL_RESULT]; /* some of the items found */
};
/* sunos 4.1.3 does not declare these */
#if defined(SUNOS41) && defined(__cplusplus)
extern "C" {
void bzero(char*, int);
int socket(int, int, int);
int bind(int, const void *, int);
}
#endif
#endif /* GRID_BASIC_H */
/* --------------------------------------------------------------- */
/* -- Copyright (c) 1994, 1995 Computer Sciences Department, -- */
/* -- University of Wisconsin-Madison, subject to the terms -- */
/* -- and conditions given in the file COPYRIGHT. All Rights -- */
/* -- Reserved. -- */
/* --------------------------------------------------------------- */
#define GRID_BASICS_C
#include "ShoreConfig.h"
#include "string.h"
#include "grid_basics.h"
item_t::item_t()
: x(0), y(0)
{
memset(name, 0, MAX_NAME_LEN);
}
item_t::item_t(const char* _name, int _x, int _y)
: x(_x), y(_y)
{
strncpy(name, _name, MAX_NAME_LEN);
name[MAX_NAME_LEN] = 0; // make sure string ends in zero
}
/* --------------------------------------------------------------- */
/* -- Copyright (c) 1994, 1995 Computer Sciences Department, -- */
/* -- University of Wisconsin-Madison, subject to the terms -- */
/* -- and conditions given in the file COPYRIGHT. All Rights -- */
/* -- Reserved. -- */
/* --------------------------------------------------------------- */
#ifndef COMMAND_H
#define COMMAND_H
typedef char* cmd_err_t;
/*
* Command processing class
*/
class command_base_t {
public:
command_base_t() {};
virtual ~command_base_t() {};
// Commands that get converted to an RPC
// All return an error message string that is NULL if success.
virtual cmd_err_t commit_transaction() = 0;
virtual cmd_err_t abort_transaction() = 0;
virtual cmd_err_t clear_grid() = 0;
virtual cmd_err_t print_grid(grid_display_t& rows) = 0;
virtual cmd_err_t add_item(const char* name, int x, int y) = 0;
virtual cmd_err_t remove_item(const char* name) = 0;
virtual cmd_err_t move_item(const char* name, int x, int y) = 0;
virtual cmd_err_t location_of(const char* name, int& x, int& y) = 0;
virtual cmd_err_t spatial_query(const nbox_t& box, spatial_result_t& result) = 0;
// Command Parsing
// Print errors to stderr
// Sets "quit" to true if quit command was found
void parse_command(char* line, bool& quit);
};
#endif /* COMMAND_H */
/* --------------------------------------------------------------- */
/* -- Copyright (c) 1994, 1995 Computer Sciences Department, -- */
/* -- University of Wisconsin-Madison, subject to the terms -- */
/* -- and conditions given in the file COPYRIGHT. All Rights -- */
/* -- Reserved. -- */
/* --------------------------------------------------------------- */
/*
* This file implements the main() code for the grid client program
*/
#include "ShoreConfig.h"
#include <stream.h>
#include <string.h>
#include <strstream.h>
#include <ctype.h>
#include <rpc/rpc.h>
// include stuff needed for SM applications (clients)
// use this rather than sm_vas.h since it's small and all that
// is necessary for this file
#include "grid_basics.h"
#include "sm_app.h"
#include "msg.h"
#include "grid.h"
#include "command.h"
enum command_token_t {
commit_cmd,
abort_cmd,
clear_cmd,
print_cmd,
add_cmd,
remove_cmd,
move_cmd,
locate_cmd,
spatial_cmd,
quit_cmd,
help_cmd
};
struct command_description_t {
command_token_t token;
int param_cnt; // number of parameters
char* name; // string name of command
char* parameters; // parameter list
char* description; // command description
};
static command_description_t descriptions[] = {
{commit_cmd, 0, "commit", "", "commit transaction and start another one"},
{abort_cmd, 0, "abort", "", "abort transaction and start another one"},
{clear_cmd, 0, "clear", "", "clear grid"},
{print_cmd, 0, "print", "", "print grid"},
{add_cmd, 3, "add", "name x y", "add new item <name> at <x,y>"},
{remove_cmd, 1, "remove", "name", "remove item <name>"},
{move_cmd, 3, "move", "name x y", "move item <name> to location <x,y>"},
{locate_cmd, 1, "locate", "name", "print location of item <name>"},
{spatial_cmd, 4, "spatial", "x_lo y_lo x_hi y_hi", "print count of items in rectangle and list first few items"},
{quit_cmd, 0, "quit", "", "quit and exit program (aborts current transaction)"},
{help_cmd, 0, "help", "", "prints this message"}
};
// number of commands
static command_cnt = sizeof(descriptions)/sizeof(command_description_t);
static void
print_commands()
{
cerr << "Valid commands are: \n"<< endl;
const command_description_t* cmd;
for (cmd = descriptions; cmd != descriptions+command_cnt; cmd++) {
cerr << " " << cmd->name << " " << cmd->parameters << endl;
cerr << " " << cmd->description << endl;
}
cerr << "\n Comments begin with a '#' and continue until the end of the line." << endl;
}
static void
print_usage(const command_description_t* cmd)
{
cerr << "Usage: "<< cmd->name << " " << cmd->parameters << endl;
}
void
command_base_t::parse_command(char* line, bool& quit)
{
istrstream s(line);
const max_params = 5;
char* params[max_params];
int param_cnt = 0;
int i;
// find all parameters in the line (parameters begin
// with non-white space) and end each parameter with \0
bool in_param = false; // not current in a parameter
for (i = 0; line[i] != '\0'; i++) {
if (in_param) {
if (isspace(line[i])) {
// end of parameter
line[i] = '\0';
in_param = false;
}
} else {
if (line[i] == '#') {
// rest of line is comment
break;
}
if (!isspace(line[i])) {
// beginning of parameter
if (param_cnt == max_params) {
cerr << "Error: too many parameters." << endl;
return;
}
params[param_cnt] = line+i;
param_cnt++;
in_param = true;
}
}
}
if (param_cnt == 0) {
// blank line
return;
}
// Search for command in command list
command_description_t* cmd;
for (cmd = descriptions; cmd != descriptions+command_cnt; cmd++) {
// command is recognized with just first 2 characters
if (strncmp(params[0], cmd->name, 2) == 0) {
break;
}
}
if (cmd == descriptions+command_cnt) {
// command not found
cerr << "Error: unkown command " << params[0] << endl;
print_commands();
} else if (cmd->param_cnt != param_cnt-1) {
// wrong number of parameters
cerr << "Error: wrong number of parameters for " << cmd->name << endl;
print_usage(cmd);
} else {
// call proper RPC for the command
int x;
int y;
char* name;
quit = false;
cmd_err_t err = 0;
switch(cmd->token) {
case commit_cmd:
err = commit_transaction();
if (!err) {
cout << "transaction is committed -- new one started" << endl;
}
break;
case abort_cmd:
err = abort_transaction();
if (!err) {
cout << "transaction is rolled back -- new one started" << endl;
}
break;
case clear_cmd:
err = clear_grid();
if (!err) {
cout << "grid has been cleared" << endl;
}
break;
case print_cmd:
grid_display_t display;
err = print_grid(display);
if (err) break;
// print header line
cout << "\n ";
for (int col = 0; col < MAX_GRID_X; col++) {
if (col%10 == 0) {
cout << '.';
} else {
cout << col%10;
}
}
cout << endl;
// print rows
for (int row = 0; row < MAX_GRID_Y; row++) {
cout << form("%.3i", row) << " " ;
for (int col = 0; col < MAX_GRID_X; col++) {
cout << display.rows[row][col];
}
cout << endl;
}
break;
case add_cmd:
name = params[1];
x = strtol(params[2], 0, 0);
y = strtol(params[3], 0, 0);
if (x < 0 || x >= MAX_GRID_X) {
cerr << "Error: x parameter must be >=0 and < " << MAX_GRID_X << endl;
break;
}
if (y < 0 || y >= MAX_GRID_Y) {
cerr << "Error: y parameter must be >=0 and < " << MAX_GRID_Y << endl;
break;
}
err = add_item(name, x, y);
if (!err) {
cout << "new item " << name << " has been added" << endl;
}
break;
case remove_cmd:
name = params[1];
err = remove_item(name);
if (!err) {
cout << "item " << name << " has been removed" << endl;
}
break;
case move_cmd:
name = params[1];
x = strtol(params[2], 0, 0);
y = strtol(params[3], 0, 0);
if (x < 0 || x >= MAX_GRID_X) {
cerr << "Error: x parameter must be >=0 and < " << MAX_GRID_X << endl;
break;
}
if (y < 0 || y >= MAX_GRID_Y) {
cerr << "Error: y parameter must be >=0 and < " << MAX_GRID_Y << endl;
break;
}
err = move_item(name, x, y);
if (!err) {
cout << "item " << name << " has been moved" << endl;
}
break;
case locate_cmd:
name = params[1];
err = location_of(name, x, y);
if (!err) {
cout << "item " << name << " is located at: "
<< x << "," << y << endl;
}
break;
case spatial_cmd: {
// generate nbox from last 4 parameters
const coord_cnt = 4;
int coord[coord_cnt];
int i;
for (i = 0; i < coord_cnt; i++ ) {
coord[i] = strtol(params[i+1], 0, 0);
}
{
nbox_t box(2, coord);
spatial_result_t result;
err = spatial_query(box, result);
if (!err) {
cout << "In box [" << coord[0] << "," << coord[1]
<< " " << coord[2] << "," << coord[3] << "]"
<< " there are " << result.found_cnt << " items."
<< endl;
if (result.found_cnt > 0) {
int print_cnt = MIN(MAX_SPATIAL_RESULT, result.found_cnt);
cout << "The first " << print_cnt
<< " items found are:" << endl;
item_t* it; // item iterator
for (i = 0, it = result.items;
i < print_cnt; i++, it++) {
cout << it->name << " " << it->x << "," << it->y << endl;
}
}
}
}
}
break;
case quit_cmd:
quit = true;
break;
case help_cmd:
print_commands();
break;
default:
cerr << "Internal Error at: " << __FILE__ << ":" << __LINE__ << endl;
exit(1);
}
if (err) {
//cerr << "Error: " << err << endl;
cerr << err << endl;
cerr << "Error: " << cmd->name << " command failed." << endl;
}
}
}
/* --------------------------------------------------------------- */
/* -- Copyright (c) 1994, 1995 Computer Sciences Department, -- */
/* -- University of Wisconsin-Madison, subject to the terms -- */
/* -- and conditions given in the file COPYRIGHT. All Rights -- */
/* -- Reserved. -- */
/* --------------------------------------------------------------- */
/*
* This file implements configuration option processing for
* both the client and the server.
*/
#include <stream.h>
#include <string.h>
// since this file only deals with the SSM option package,
// rather than including sm_vas.h, just include what's needed for
// options:
#include "w.h"
#include "option.h"
/*
* init_config_options intialized configuration options for
* both the client and server programs in the Grid example.
*
* The options parameter is the option group holding all the options.
* It is assumed that all SSM options have been added if called
* by the server.
*
* The prog_type parameter is should be either "client" or "server".
*
* The argc and argv parameters should be argc and argv from main().
* Recognized options will be located in argv and removed. argc
* is changed to reflect the removal.
*
*/
w_rc_t
init_config_options(option_group_t& options,
const char* prog_type,
int& argc, char** argv)
{
w_rc_t rc; // return code
// set prog_name to the file name of the program without the path
char* prog_name = strrchr(argv[0], '/');
if (prog_name == NULL) {
prog_name = argv[0];
} else {
prog_name += 1; /* skip the '/' */
if (prog_name[0] == '\0') {
prog_name = argv[0];
}
}
W_DO(options.add_class_level("grid")); // for all grid examples
W_DO(options.add_class_level(prog_type)); // server or client
W_DO(options.add_class_level(prog_name)); // program name
// read the .examplerc file to set options
{
ostrstream err_stream;
const char* opt_file = "./exampleconfig"; // option config file
option_file_scan_t opt_scan(opt_file, &options);
// scan the file and override any current option settings
// options names must be spelled correctly
rc = opt_scan.scan(true /*override*/, err_stream, true);
if (rc) {
char* errmsg = err_stream.str();
cerr << "Error in reading option file: " << opt_file << endl;
cerr << "\t" << errmsg << endl;
if (errmsg) delete errmsg;
return rc;
}
}
// parce argv for options
if (!rc) {
// parse command line
ostrstream err_stream;
rc = options.parse_command_line(argv, argc, 2, &err_stream);
err_stream << ends;
char* errmsg = err_stream.str();
if (rc) {
cerr << "Error on Command line " << endl;
cerr << "\t" << w_error_t::error_string(rc.err_num()) << endl;
cerr << "\t" << errmsg << endl;
return rc;
}
if (errmsg) delete errmsg;
}
// check required options
{
ostrstream err_stream;
rc = options.check_required(&err_stream);
if (rc) {
char* errmsg = err_stream.str();
cerr << "These required options are not set:" << endl;
cerr << errmsg << endl;
if (errmsg) delete errmsg;
return rc;
}
}
return RCOK;
}
/* --------------------------------------------------------------- */
/* -- Copyright (c) 1994, 1995 Computer Sciences Department, -- */
/* -- University of Wisconsin-Madison, subject to the terms -- */
/* -- and conditions given in the file COPYRIGHT. All Rights -- */
/* -- Reserved. -- */
/* --------------------------------------------------------------- */
/*
* This file implements the main() code for the grid server program
*/
#include "ShoreConfig.h"
#include <stream.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <rpc/rpc.h>
#ifdef SOLARIS2
#include <rpc/svc_soc.h>
#endif
// This include brings in all header files needed for writing a VAs
#include "sm_vas.h"
#include "grid_basics.h"
#define RPC_SVC
#include "msg.h"
#include "grid.h"
#include "command.h"
#include "command_server.h"
#include "rpc_thread.h"
ss_m* ssm = 0;
// shorten error code type name
typedef w_rc_t rc_t;
// this is implemented in options.C
w_rc_t init_config_options(option_group_t& options,
const char* prog_type,
int& argc, char** argv);
// pointer to RPC service this server provides
SVCXPRT* svcxprt = 0;
/*
* This function either formats a new device and creates a
* volume on it, or mounts an already existing device and
* returns the ID of the volume on it.
*/
rc_t
setup_device_and_volume(const char* device_name, bool init_device,
smksize_t quota, lvid_t& lvid)
{
devid_t devid;
u_int vol_cnt;
rc_t rc;
if (init_device) {
cout << "Formatting and mounting device: " << device_name
<< " with a " << quota << "KB quota ..." << endl;
W_DO(ssm->format_dev(device_name, quota, true));
// mount the new device
W_DO(ssm->mount_dev(device_name, vol_cnt, devid));
// generate a volume ID for the new volume we are about to
// create on the device
W_DO(ssm->generate_new_lvid(lvid));
// create the new volume
cout << "Creating a new volume on the device" << endl;
cout << " with a " << quota << "KB quota ..." << endl;
W_DO(ssm->create_vol(device_name, lvid, quota));
// create the logical ID index on the volume, reserving no IDs
W_DO(ssm->add_logical_id_index(lvid, 0, 0));
} else {
cout << "Using already existing device: " << device_name << endl;
// mount already existing device
rc = ssm->mount_dev(device_name, vol_cnt, devid);
if (rc) {
cerr << "Error: could not mount device: " << device_name << endl;
cerr << " Did you forget to run the server with -i the first time?" << endl;
return rc;
}
// find ID of the volume on the device
lvid_t* lvid_list;
u_int lvid_cnt;
W_DO(ssm->list_volumes(device_name, lvid_list, lvid_cnt));
if (lvid_cnt == 0) {
cerr << "Grid program error, device has no volumes" << endl;
::exit(1);
}
lvid = lvid_list[0];
delete [] lvid_list;
}
return RCOK;
}
/*
* This function starts the RPC service by allocating a connection
* socket (and binding it to conn_port) and calling RPC initialization
* functions.
*/
rc_t
start_tcp_rpc(int conn_port, int& conn_sock)
{
struct sockaddr_in addr;
cerr << "allocating a tcp socket for listening for connections ..." << endl;
#ifdef SOLARIS2
conn_sock = t_open("/dev/tcp", O_RDWR, 0);
#else
conn_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
#endif
if (conn_sock < 0) {
perror("socket");
return RC(fcOS); // indicate an OS error occurred
}
cerr << "binding to port " << conn_port << endl;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(conn_port);
#ifdef SOLARIS2
// TODO: what is the tli counterpart of SO_REUSEADDR?
struct t_bind tb_args;
tb_args.addr.maxlen = tb_args.addr.len = sizeof addr;
tb_args.addr.buf = (char *)&addr;
tb_args.qlen = 5; // Arbitrary value
if (t_bind(conn_sock, &tb_args, 0) < 0) {
// TODO: what is the tli counterpart of SO_REUSEADDR?
// TODO: deal with Address-in-use error
perror("t_bind");
return 0;
}
#else
if (bind(conn_sock, (struct sockaddr *)&addr, sizeof (addr)) < 0) {
perror("bind");
return RC(fcOS);
}
#endif
cerr << "creating tcp service" << endl;
int buf_size = 0; // use default send/receive buffer size
errno = 0;
svcxprt = svctcp_create(conn_sock, buf_size, buf_size);
if(svcxprt == 0) {
cerr << "Error: svctcp_create returned NULL" << endl;
if (errno) return RC(fcOS);
else return RC(fcINTERNAL);
}
cerr << "registering rpc service" << endl;
// pass 0 for the protocol parameter so that the portmapper is not
// used.
if (!svc_register(svcxprt, GRID, GRIDVERS, grid_1, 0/*protocol*/)) {
// registration failed
cerr << "Error: rpc registration failed" << endl;
::exit(1);
// Note: if we were registering with the portmapper
// we could call svc_unregister and then
// try again.
}
return RCOK;
}
/*
* This function ends the RPC service by calling RPC shutdown functions.
*/
rc_t
stop_tcp_rpc()
{
assert(svcxprt);
cerr << "unregister rpc service" << endl;
svc_unregister(GRID, GRIDVERS);
cerr << "destroy rpc service" << endl;
svc_destroy(svcxprt);
svcxprt = 0;
return RCOK;
}
void
usage(option_group_t& options)
{
cerr << "Usage: server [-i] [options]" << endl;
cerr << " -i will re-initialize the device/volume for the DB" << endl;
cerr << "Valid options are: " << endl;
options.print_usage(TRUE, cerr);
}
class startup_smthread_t : public smthread_t
{
private:
option_t* opt_connect_port,
* opt_device_name,
* opt_device_quota;
bool init_device;
public:
startup_smthread_t(option_t *, option_t *, option_t *, bool);
~startup_smthread_t() {}
void run();
};
startup_smthread_t::startup_smthread_t(
option_t * _opt_connect_port,
option_t * _opt_device_name,
option_t * _opt_device_quota,
bool _init_device
)
:
opt_connect_port(_opt_connect_port),
opt_device_name(_opt_device_name),
opt_device_quota(_opt_device_quota),
init_device(_init_device),
smthread_t(t_regular)
{
}
int
main(int argc, char* argv[])
{
option_t* opt_connect_port = 0;
option_t* opt_device_name = 0;
option_t* opt_device_quota = 0;
// pointers to options we will create for the grid server program
cout << "processing configuration options ..." << endl;
const option_level_cnt = 3;
option_group_t options(option_level_cnt);
W_COERCE(options.add_option("connect_port", "1024 < integer < 65535",
"1234", "port for connecting to grid server",
false, option_t::set_value_long,
opt_connect_port));
W_COERCE(options.add_option("device_name", "device/file name",
NULL, "device containg volume to use for grid program",
true, option_t::set_value_charstr,
opt_device_name));
W_COERCE(options.add_option("device_quota", "# > 1000",
"2000", "quota for device containing grid volume",
false, option_t::set_value_long,
opt_device_quota));
// have the SSM add its options to the group
W_COERCE(ss_m::setup_options(&options));
if (init_config_options(options, "server", argc, argv)) {
usage(options);
::exit(1);
}
// process command line: looking for the "-i" flag
bool init_device = false;
if (argc > 2) {
usage(options);
::exit(1);
} else if (argc == 2) {
if (strcmp(argv[1], "-i") == 0) {
cout << "Do you really want to initialize the Grid database? ";
char answer;
cin >> answer;
if (answer == 'y' || answer == 'Y') {
init_device = true;
} else {
cerr << "Please try again without the -i option" << endl;
::exit(0);
}
} else {
usage(options);
::exit(1);
}
}
startup_smthread_t *doit = new startup_smthread_t( opt_connect_port,
opt_device_name, opt_device_quota, init_device);
if(!doit) {
W_FATAL(fcOUTOFMEMORY);
}
W_COERCE(doit->fork());
W_COERCE(doit->wait());
delete doit;
}
void
startup_smthread_t::run()
{
rc_t rc;
cout << "Starting SSM and performing recovery ..." << endl;
ssm = new ss_m();
if (!ssm) {
cerr << "Error: Out of memory for ss_m" << endl;
::exit(1);
}
lvid_t lvid; // ID of volume for storing grid
smksize_t quota = strtol(opt_device_quota->value(), 0, 0);
rc = setup_device_and_volume(opt_device_name->value(), init_device, quota, lvid);
if (rc) {
cerr << "could not setup device/volume due to: " << endl;
cerr << rc << endl;
delete ssm;
rc = RCOK; // force deletion of w_error_t info hanging off rc
// otherwise a leak for w_error_t will be reported
::exit(1);
}
// tell the command server what volume to use for the grid
command_server_t::lvid = lvid;
// start the RPC service listening on the connection port
// specified by the connect_port option
int connect_port = strtol(opt_connect_port->value(), 0, 0);
cerr << "starting up, listening on port " << connect_port <<endl;
int connect_socket;
W_COERCE(start_tcp_rpc(connect_port, connect_socket));
listener_t* listen_thread = new listener_t(connect_socket);
W_COERCE(listen_thread->fork());
// start thread to process commands on stdin
cout << "main starting stdin thread" << endl;
stdin_thread_t* stdin_thread = new stdin_thread_t;
W_COERCE(stdin_thread->fork());
// wait for the stdin thread to finish
W_COERCE(stdin_thread->wait());
cout << "Stdin thread is done" << endl;
// shutdown the RPC listener thread and wait for it to end
listen_thread->shutdown();
W_COERCE(listen_thread->wait());
delete listen_thread;
delete stdin_thread;
W_COERCE(stop_tcp_rpc());
cout << "\nShutting down SSM ..." << endl;
delete ssm;
cout << "Finished!" << endl;
}
/* --------------------------------------------------------------- */
/* -- Copyright (c) 1994, 1995 Computer Sciences Department, -- */
/* -- University of Wisconsin-Madison, subject to the terms -- */
/* -- and conditions given in the file COPYRIGHT. All Rights -- */
/* -- Reserved. -- */
/* --------------------------------------------------------------- */
#ifndef COMMAND_SERVER_H
#define COMMAND_SERVER_H
/*
* Server command processing class
*/
class command_server_t : public command_base_t {
public:
command_server_t();
~command_server_t();
// RPC methods
virtual cmd_err_t commit_transaction();
virtual cmd_err_t abort_transaction();
virtual cmd_err_t clear_grid();
virtual cmd_err_t print_grid(grid_display_t& rows);
virtual cmd_err_t add_item(const char* name, int x, int y);
virtual cmd_err_t remove_item(const char* name);
virtual cmd_err_t move_item(const char* name, int x, int y);
virtual cmd_err_t location_of(const char* name, int& x, int& y);
virtual cmd_err_t spatial_query(const nbox_t& box, spatial_result_t& result);
static lvid_t lvid; // volume containing grid
private:
static const char* grid_name; // root name of grid
rc_t init();
grid_t grid; // grid to serve requests for
// These are used to generate error replies
ostrstream err_strstream;
char _err_space[MAX_ERR_MSG_LEN];
};
#endif /* COMMAND_SERVER_H */
/* --------------------------------------------------------------- */
/* -- Copyright (c) 1994, 1995 Computer Sciences Department, -- */
/* -- University of Wisconsin-Madison, subject to the terms -- */
/* -- and conditions given in the file COPYRIGHT. All Rights -- */
/* -- Reserved. -- */
/* --------------------------------------------------------------- */
/*
* This file implements the server side of the RPCs
*
* It is organized as a mix of RPC stub and corresponding
* command_server_t method for the stub.
*/
#include "ShoreConfig.h"
#include <stream.h>
#include <string.h>
#include <rpc/rpc.h>
// include stuff needed for SM applications (clients)
#include "sm_vas.h"
#include "grid_basics.h"
#define RPC_SVC /* so rpc prototypes are included */
#include "msg.h"
#include "grid.h"
#include "command.h"
#include "command_server.h"
#include "rpc_thread.h"
lvid_t command_server_t::lvid = lvid_t::null;
const char* command_server_t::grid_name = "Grid_Name";
/*
* This is an error handling macro that calls "method".
* If "method" returns an error, a message is written to the
* command_server_t err_strstream and the string message is returned.
*/
#define SSMDO(method) \
{ \
w_rc_t __e = method; \
if (__e) { \
cerr << __e << endl; \
err_strstream.seekp(0, ios::beg); \
err_strstream << "Error from SSM: " \
<< __e->error_string(__e.err_num()) << ends; \
return err_strstream.str(); \
} \
}
/********************************************************************
Command_server_t Methods
*******************************************************************/
command_server_t::command_server_t()
: err_strstream(_err_space, sizeof(_err_space))
{
W_COERCE(ss_m::begin_xct());
rc_t rc = init();
if (rc) {
cerr << "Error: could not start command server due to:" << endl;
cerr << " " << rc << endl;
W_COERCE(ss_m::abort_xct());
exit(1);
}
W_COERCE(ss_m::commit_xct()); // commit the board initialization
// start the first transaction for this client
W_COERCE(ss_m::begin_xct());
}
command_server_t::~command_server_t()
{
// connection is shutting down, so
// abort the currently running transaction
W_COERCE(ss_m::abort_xct());
}
/*
* This functions initialized a command_server_t, including
* creating the grid database structures if they do not yet exist.
*
* First, it sees if there is a grid on the volume by
* looking up the special string, Grid_Name, in
* the volume root index.
*
* If no grid already exists, we create one. And store info
* about it in the root index.
*/
rc_t
command_server_t::init()
{
serial_t root_iid; // root index ID
W_DO(ss_m::vol_root_index(lvid, root_iid));
grid_t::grid_info_t info;
smsize_t info_len = sizeof(info);
bool found;
W_DO(ss_m::find_assoc(lvid, root_iid,
vec_t(grid_name, strlen(grid_name)),
&info, info_len, found));
if (found) {
assert(info_len == sizeof(info));
cout << "Using already existing grid" << endl;
} else {
cout << "Creating a new Grid" << endl;
// create the item file
W_DO(ss_m::create_file(lvid, info.item_file, t_regular));
// create the btree index on item name
// the "b*1000" indicates the key type is a variable
// length byte string with maximum length of 1000
W_DO(ss_m::create_index(lvid, ss_m::t_uni_btree, t_regular,
"b*1000", 0, info.name_index));
// create the R*tree index on item location
W_DO(ss_m::create_md_index(lvid, ss_m::t_rtree, t_regular, info.spatial_index));
// store the grid info in the root index
W_DO(ss_m::create_assoc(lvid, root_iid,
vec_t(grid_name, strlen(grid_name)),
vec_t(&info, sizeof(info))));
}
grid.init(lvid, info);
return RCOK;
}
/********************************************************************
The following command_server_t methods correspond to RPCs
*******************************************************************/
cmd_err_t
command_server_t::commit_transaction()
{
SSMDO(ss_m::commit_xct());
SSMDO(ss_m::begin_xct());
return 0; /* success */
}
cmd_err_t
command_server_t::abort_transaction()
{
SSMDO(ss_m::abort_xct());
SSMDO(ss_m::begin_xct());
return 0; /* success */
}
cmd_err_t
command_server_t::clear_grid()
{
SSMDO(grid.clear());
return 0; /* success */
}
cmd_err_t
command_server_t::print_grid(grid_display_t& rows)
{
SSMDO(grid.generate_display(rows));
return 0; /* success */
}
cmd_err_t
command_server_t::add_item(const char* name, int x, int y)
{
SSMDO(grid.add_item(name, x, y));
return 0; /* success */
}
cmd_err_t
command_server_t::remove_item(const char* name)
{
bool found;
SSMDO(grid.remove_item(name, found));
if (!found) {
err_strstream.seekp(0, ios::beg);
err_strstream << "Error: item was not found" << endl;
return err_strstream.str();
}
return 0; /* success */
}
cmd_err_t
command_server_t::move_item(const char* name, int x, int y)
{
bool found;
SSMDO(grid.move_item(name, x, y, found));
if (!found) {
err_strstream.seekp(0, ios::beg);
err_strstream << "Error: item was not found" << endl;
return err_strstream.str();
}
return 0; /* success */
}
cmd_err_t
command_server_t::location_of(const char* name, int& x, int& y)
{
bool found;
SSMDO(grid.location_of(name, x, y, found));
if (!found) {
err_strstream.seekp(0, ios::beg);
err_strstream << "Error: item was not found" << endl;
return err_strstream.str();
}
return 0; /* success */
}
cmd_err_t
command_server_t::spatial_query(const nbox_t& box, spatial_result_t& result)
{
SSMDO(grid.spatial_query(box, result));
return 0; /* success */
}
/* --------------------------------------------------------------- */
/* -- Copyright (c) 1994, 1995 Computer Sciences Department, -- */
/* -- University of Wisconsin-Madison, subject to the terms -- */
/* -- and conditions given in the file COPYRIGHT. All Rights -- */
/* -- Reserved. -- */
/* --------------------------------------------------------------- */
/*
* This file implements the server side of the RPCs
*
* It is organized as a mix of RPC stub and corresponding
* command_server_t method for the stub.
*/
#include "ShoreConfig.h"
/*
* NB: THIS IS IMPORTANT: we include "ShoreConfig.h" because
* we *NEED* the system-dependent definition of a jmp_buf
* in order to see that the thread data structures that
* that we build here are consistent with those in the library.
* If we don't get the right #defines for the configuration, we
* run the risk of building .o files here with the wrong idea
* about the size of sthread_t (the root of the class hierarchy
* for our threads).
*/
#include <stream.h>
#include <string.h>
#include <rpc/rpc.h>
// include stuff needed for SM applications (clients)
#include "sm_vas.h"
#include "grid_basics.h"
#define RPC_SVC /* so rpc prototypes are included */
#include "msg.h"
#include "grid.h"
#include "command.h"
#include "command_server.h"
#include "rpc_thread.h"
/********************************************************************
RPC Stubs that call command_server_t methods for processing
*******************************************************************/
// there is no command_server_t method for ping
void *
ping_rpc_1(void* , svc_req* )
{
return (void *)client_t::me()->reply_buf;
}
error_reply *
commit_transaction_rpc_1(void* , svc_req* )
{
client_t* Me = client_t::me();
error_reply& reply = *(error_reply*)Me->reply_buf;
cmd_err_t err = Me->command_server->commit_transaction();
if (err) {
strncpy(reply.error_msg, err, MAX_ERR_MSG_LEN);
reply.error_msg[MAX_ERR_MSG_LEN-1] = '\0';
} else {
// no error message
reply.error_msg[0] = '\0';
}
return &reply;
}
error_reply *
abort_transaction_rpc_1(void* , svc_req* )
{
client_t* Me = client_t::me();
error_reply& reply = *(error_reply*)Me->reply_buf;
cmd_err_t err = Me->command_server->abort_transaction();
if (err) {
strncpy(reply.error_msg, err, MAX_ERR_MSG_LEN);
reply.error_msg[MAX_ERR_MSG_LEN-1] = '\0';
} else {
// no error message
reply.error_msg[0] = '\0';
}
return &reply;
}
error_reply *
clear_grid_rpc_1(void* , svc_req* )
{
client_t* Me = client_t::me();
error_reply& reply = *(error_reply*)Me->reply_buf;
cmd_err_t err = Me->command_server->clear_grid();
if (err) {
strncpy(reply.error_msg, err, MAX_ERR_MSG_LEN);
reply.error_msg[MAX_ERR_MSG_LEN-1] = '\0';
} else {
// no error message
reply.error_msg[0] = '\0';
}
return &reply;
}
print_grid_reply *
print_grid_rpc_1(void* , svc_req* )
{
client_t* Me = client_t::me();
print_grid_reply& reply = *(print_grid_reply*)Me->reply_buf;
cmd_err_t err = Me->command_server->print_grid(reply.display);
if (err) {
strncpy(reply.error_msg, err, MAX_ERR_MSG_LEN);
reply.error_msg[MAX_ERR_MSG_LEN-1] = '\0';
} else {
// no error message
reply.error_msg[0] = '\0';
}
return &reply;
}
error_reply *
add_item_rpc_1(add_item_arg* argp, svc_req* )
{
client_t* Me = client_t::me();
error_reply& reply = *(error_reply*)Me->reply_buf;
cmd_err_t err = Me->command_server->add_item(argp->name, argp->x, argp->y);
if (err) {
strncpy(reply.error_msg, err, MAX_ERR_MSG_LEN);
reply.error_msg[MAX_ERR_MSG_LEN-1] = '\0';
} else {
// no error message
reply.error_msg[0] = '\0';
}
return &reply;
}
error_reply *
remove_item_rpc_1(remove_item_arg* argp, svc_req* )
{
client_t* Me = client_t::me();
error_reply& reply = *(error_reply*)Me->reply_buf;
cmd_err_t err = Me->command_server->remove_item(argp->name);
if (err) {
strncpy(reply.error_msg, err, MAX_ERR_MSG_LEN);
reply.error_msg[MAX_ERR_MSG_LEN-1] = '\0';
} else {
// no error message
reply.error_msg[0] = '\0';
}
return &reply;
}
error_reply *
move_item_rpc_1(add_item_arg* argp, svc_req* )
{
client_t* Me = client_t::me();
error_reply& reply = *(error_reply*)Me->reply_buf;
cmd_err_t err = Me->command_server->move_item(argp->name, argp->x, argp->y);
if (err) {
strncpy(reply.error_msg, err, MAX_ERR_MSG_LEN);
reply.error_msg[MAX_ERR_MSG_LEN-1] = '\0';
} else {
// no error message
reply.error_msg[0] = '\0';
}
return &reply;
}
location_reply *
location_of_rpc_1(location_arg* argp, svc_req* )
{
client_t* Me = client_t::me();
location_reply& reply = *(location_reply*)Me->reply_buf;
cmd_err_t err = Me->command_server->location_of(argp->name, reply.x, reply.y);
if (err) {
strncpy(reply.error_msg, err, MAX_ERR_MSG_LEN);
reply.error_msg[MAX_ERR_MSG_LEN-1] = '\0';
} else {
// no error message
reply.error_msg[0] = '\0';
}
return &reply;
}
spatial_reply *
spatial_rpc_1(spatial_arg* argp, svc_req* )
{
client_t* Me = client_t::me();
spatial_reply& reply = *(spatial_reply*)Me->reply_buf;
// the spatial_arg is really an array of integers in the format
// for an nbox_t
nbox_t box(2, (int*)(&argp->x_low));
cmd_err_t err = Me->command_server->spatial_query(box, reply.result);
if (err) {
strncpy(reply.error_msg, err, MAX_ERR_MSG_LEN);
reply.error_msg[MAX_ERR_MSG_LEN-1] = '\0';
} else {
// no error message
reply.error_msg[0] = '\0';
}
return &reply;
}
/* --------------------------------------------------------------- */
/* -- Copyright (c) 1994, 1995 Computer Sciences Department, -- */
/* -- University of Wisconsin-Madison, subject to the terms -- */
/* -- and conditions given in the file COPYRIGHT. All Rights -- */
/* -- Reserved. -- */
/* --------------------------------------------------------------- */
#ifndef GRID_H
#define GRID_H
/*
* Class managing grid and items on the grid
*/
class grid_t {
public:
struct grid_info_t {
serial_t item_file; // ID of file of item records
serial_t name_index; // B+tree mapping names to item records
serial_t spatial_index; // R*tree mapping points to items
};
grid_t();
~grid_t();
// Access an already created grid
void init(const lvid_t& lvid, const grid_info_t& info);
rc_t add_item(const char* name, int x, int y);
rc_t remove_item(const char* name, bool& found);
rc_t move_item(const char* name, int x, int y, bool& found);
rc_t location_of(const char* name, int& x, int& y, bool& found);
rc_t generate_display(grid_display_t& display);
rc_t spatial_query(const nbox_t& box, spatial_result_t& result);
rc_t clear(); // remove all items from grid
bool is_initialized() const {return lvid != lvid_t::null;}
private:
lvid_t lvid; // ID of volume containing grid;
grid_info_t info;
// get the ID of the item "name", return serial_t::null if
// not found.
rc_t get_id(const char* name, serial_t& id);
};
#endif /* GRID_H */
/* --------------------------------------------------------------- */
/* -- Copyright (c) 1994, 1995 Computer Sciences Department, -- */
/* -- University of Wisconsin-Madison, subject to the terms -- */
/* -- and conditions given in the file COPYRIGHT. All Rights -- */
/* -- Reserved. -- */
/* --------------------------------------------------------------- */
#define GRID_C
#include "ShoreConfig.h"
#include <assert.h>
#include "sm_vas.h"
#include "grid_basics.h"
#include "grid.h"
// storage manger to use for all operations
extern ss_m* ssm;
/*
* Prepare to create a new grid
*/
grid_t::grid_t()
{
}
grid_t::~grid_t()
{
// make sure we can't reuse this space
lvid = lvid_t::null;
}
/*
* Access an already created grid
*/
void
grid_t::init(const lvid_t& _lvid, const grid_info_t& _info)
{
lvid = _lvid;
info = _info;
}
rc_t
grid_t::add_item(const char* name, int x, int y)
{
assert(is_initialized());
assert(strlen(name) <= MAX_NAME_LEN);
rc_t rc;
// create a savepoint, so that if any operation fails
// we can roll back to this point;
sm_save_point_t save_point;
W_DO(ss_m::save_work(save_point));
// create record for item
item_t item(name, x, y);
serial_t item_id;
do {
// create the item record
//
// note: the use of anonymous vectors since none of the data
// to store is scattered in memory
// note: the ugly parameter comments are used because of a gcc bug
rc = ssm->create_rec(lvid, info.item_file,
vec_t(), /* empty record header */
sizeof(item), /* length hint */
vec_t(&item, sizeof(item)), /* body */
item_id); /* new rec id */
if (rc) break;
// add item to index on name
rc = ssm->create_assoc(lvid, info.name_index,
vec_t(item.name, strlen(item.name)), /* key */
vec_t(&item_id, sizeof(item_id))); /* element */
if (rc) break;
//
// add item to Rtree (spatial) index
//
// coord holds the coordinates of the "rectangle" (actually a
// point) containing the item
//
int coord[4];
coord[0] = coord[2] = x;
coord[1] = coord[3] = y;
nbox_t box(2, coord);
rc = ssm->create_md_assoc(lvid, info.spatial_index, box,
vec_t(&item_id, sizeof(item_id))); // element
} while (0);
if (rc) {
// an error occurred, so rollback to the savepoint
W_DO(ss_m::rollback_work(save_point));
return rc;
}
return RCOK;
}
rc_t
grid_t::remove_item(const char* name, bool& found)
{
assert(is_initialized());
serial_t id; // ID of item to remove
W_DO(get_id(name, id));
if (id == serial_t::null) {
found = false;
return RCOK;
}
// create a savepoint, so that if any operation fails
// we can roll back to this point;
sm_save_point_t save_point;
W_DO(ss_m::save_work(save_point));
rc_t rc;
do {
// remove item from index on name
rc = ssm->destroy_assoc(lvid, info.name_index,
vec_t(name, strlen(name)),
vec_t(&id, sizeof(id)));
if (rc) break;
//
// remove from spatial index (first must pin to find coordinates)
//
int coord[4];
{
pin_i handle;
rc = handle.pin(lvid, id, 0);
if (rc) break;
const item_t* item = (const item_t*) handle.body();
coord[0] = coord[2] = item->x;
coord[1] = coord[3] = item->y;
}
nbox_t box(2, coord);
rc = ssm->destroy_md_assoc(lvid, info.spatial_index, box,
vec_t(&id, sizeof(id))); /* element */
if (rc) break;
// destroy the item
rc = ssm->destroy_rec(lvid, id);
if (rc) break;
} while (0);
if (rc) {
// an error occurred, so rollback to the savepoint
W_DO(ss_m::rollback_work(save_point));
return rc;
}
found = true;
return RCOK;
}
rc_t
grid_t::move_item(const char* name, int x, int y, bool& found)
{
assert(is_initialized());
serial_t id; // ID of item to move
W_DO(get_id(name, id));
if (id == serial_t::null) {
found = false;
return RCOK;
}
// pin the item record
pin_i handle;
W_DO(handle.pin(lvid, id, 0));
const item_t& item = *(const item_t*)handle.body();
assert(strcmp(item.name, name) == 0);
//
// Now we remove the item from the R*tree, update the
// x,y coordinates, and re-insert it
//
// In general, the only way to "update" and index entry
// is to remove it and re-insert the changed entry.
//
// We create a savepoint, so that if any operation fails
// we can roll back to this point;
sm_save_point_t save_point;
W_DO(ss_m::save_work(save_point));
rc_t rc;
do {
//
// remove from spatial index (first must pin to find coordinates)
//
int coord[4];
coord[0] = coord[2] = item.x;
coord[1] = coord[3] = item.y;
nbox_t box(2, coord);
rc = ssm->destroy_md_assoc(lvid, info.spatial_index, box,
vec_t(&id, sizeof(id))); /* element */
if (rc) break;
//
// Now we need to update the item. Note that it is illegal
// to "update-in-place", so we create a new item and
// use the update_rec method
//
// init a new item at the new location
item_t new_item("", x, y);
// update x,y in the pinned item
vec_t new_data(&new_item.x, sizeof(new_item.x) + sizeof(new_item.y));
rc = handle.update_rec((smsize_t) offsetof(item_t, x), new_data);
if (rc) break;
//
// add item back into the R*tree at new location
//
coord[0] = coord[2] = x;
coord[1] = coord[3] = y;
rc = ssm->create_md_assoc(lvid, info.spatial_index, nbox_t(2, coord),
vec_t(&id, sizeof(id))); /* element */
if (rc) break;
} while (0);
if (rc) {
// an error occurred, so rollback to the savepoint
W_DO(ss_m::rollback_work(save_point));
return rc;
}
found = true;
return RCOK;
}
rc_t
grid_t::location_of(const char* name, int& x, int& y, bool& found)
{
assert(is_initialized());
serial_t id; // ID of item to remove
W_DO(get_id(name, id));
if (id == serial_t::null) {
found = false;
return RCOK;
}
// pin the item record to get x,y
pin_i handle;
W_DO(handle.pin(lvid, id, 0));
const item_t& item = *(const item_t*)handle.body();
assert(strcmp(item.name, name) == 0);
x = item.x;
y = item.y;
found = true;
return RCOK;
}
rc_t
grid_t::generate_display(grid_display_t& display)
{
for (int row = 0; row < MAX_GRID_Y; row++) {
for (int col = 0; col < MAX_GRID_X; col++) {
display.rows[row][col] = '+';
}
}
// add first character of each item item name the display
scan_file_i scan(lvid, info.item_file);
pin_i* handle; // handle on current record
bool eof = false;
const item_t* item;
// scan item file and remove all items
W_DO(scan.next(handle, 0, eof));
while (!eof) {
item = (const item_t*)handle->body();
display.rows[item->y][item->x] = item->name[0];
W_DO(scan.next(handle, 0, eof));
}
return RCOK;
}
/*
* This method removes all items and their associated index entries
*/
rc_t
grid_t::clear()
{
// since we will eventually lock every item, we first obtain
// an exclusive lock on the file and indexes so that finer
// granularity locks are not obtained -- thus improving
// the performance of this method
W_DO(ss_m::lock(lvid, info.item_file, EX));
W_DO(ss_m::lock(lvid, info.name_index, EX));
W_DO(ss_m::lock(lvid, info.spatial_index, EX));
scan_file_i scan(lvid, info.item_file);
pin_i* handle; // handle on current record
bool eof = false;
const item_t* item;
rc_t rc;
// create a savepoint, so that if any operation fails
// we can roll back to this point;
sm_save_point_t save_point;
W_DO(ss_m::save_work(save_point));
// scan item file and remove all items
rc = scan.next(handle, 0, eof);
while (!eof && !rc) {
item = (const item_t*)handle->body();
const serial_t& id = handle->serial_no();
// remove from name index (key==name, element==serial# of item)
rc = ss_m::destroy_assoc(lvid, info.name_index,
vec_t(item->name, strlen(item->name)),
vec_t(&id, sizeof(serial_t)));
if (rc) break;
// remove from spatial index
int coord[4];
coord[0] = coord[2] = item->x;
coord[1] = coord[3] = item->y;
nbox_t box(2, coord);
rc = ssm->destroy_md_assoc(lvid, info.spatial_index, box,
vec_t(&id, sizeof(serial_t)));
if (rc) break;
// remove item itself
rc = ss_m::destroy_rec(lvid, id);
if (rc) break;
rc = scan.next(handle, 0, eof);
}
if (rc) {
// an error occurred, so rollback to the savepoint
W_DO(ss_m::rollback_work(save_point));
return rc;
}
return RCOK;
}
rc_t
grid_t::spatial_query(const nbox_t& box, spatial_result_t& result)
{
assert(is_initialized());
result.found_cnt = 0;
serial_t id;
smsize_t id_len = sizeof(id);
nbox_t key;
bool eof;
pin_i handle;
scan_rt_i scan(lvid, info.spatial_index, t_overlap, box);
W_DO(scan.next(key, &id, id_len, eof));
while (!eof) {
assert(id_len == sizeof(id));
if (result.found_cnt < MAX_SPATIAL_RESULT) {
// pin the item record to get x,y
W_DO(handle.pin(lvid, id, 0));
const item_t& item = *(const item_t*)handle.body();
strcpy(result.items[result.found_cnt].name, item.name);
result.items[result.found_cnt].x = item.x;
result.items[result.found_cnt].y = item.y;
assert(item.x == key.bound(0));
assert(item.y == key.bound(1));
}
result.found_cnt++;
W_DO(scan.next(key, &id, id_len, eof));
}
return RCOK;
}
rc_t
grid_t::get_id(const char* name, serial_t& id)
{
bool found;
// find ID (serial#) of item
// find_assoc will fill &id with the ID. For safety, we
// set id_len to sizeof(id) so that no bytes beyond id will
// be written in case we accidentally put something to large in
// the index.
smsize_t id_len = sizeof(id);
W_DO(ssm->find_assoc(lvid, info.name_index,
vec_t(name, strlen(name)),
&id, id_len, found));
if (!found) {
id = serial_t::null;
}
assert(id_len == sizeof(id));
return RCOK;
}
/* --------------------------------------------------------------- */
/* -- Copyright (c) 1994, 1995 Computer Sciences Department, -- */
/* -- University of Wisconsin-Madison, subject to the terms -- */
/* -- and conditions given in the file COPYRIGHT. All Rights -- */
/* -- Reserved. -- */
/* --------------------------------------------------------------- */
#include "ShoreConfig.h"
#ifndef RPC_THREAD_H
#define RPC_THREAD_H
class client_t;
/*
* Listener_t: Thread that listens for RPC connections from clients
* Cleaner_t: Thread that deletes defunct client threads
*/
class listener_t; // forward
class cleaner_t: public smthread_t {
private:
bool _quit;
listener_t * _listener;
public:
cleaner_t(listener_t *);
~cleaner_t() {}
void kick();
void run();
void destroy();
};
class listener_t: public smthread_t {
friend class cleaner_t;
public:
listener_t(int fd /*socket*/);
~listener_t();
void shutdown();
// called by client_t thread to indicate it is starting/done
void child_is_done(w_link_t& child_link);
private:
void run();
int _fd; // socket to listen on
sfile_read_hdl_t* _ready; // read handler for the socket
smutex_t _clients_mutex; // syncronizes access to _clients
w_list_t<client_t> _clients; // list of client_t
scond_t _clients_empty; // condition variable
cleaner_t *_cleaner_thread;
};
/*
* Thread that manages a client connection and processes RPCs
*/
class client_t: public smthread_t {
friend class cleaner_t;
public:
client_t(int fd /*socket*/, listener_t* parent);
~client_t();
void run();
// return the current running thread.
static client_t* me() { return (client_t*) smthread_t::me(); }
// put reply messages here
char reply_buf[thread_reply_buf_size];
// the command_server implements RPCs
command_server_t* command_server;
// this function returns the offset of _link for so that
// listener_t can create a list of client_t objects
static size_t link_offset() {return offsetof(client_t, _link);}
private:
int _fd; // socket for incoming RPCs
sfile_read_hdl_t* _ready; // read handler for the socket
listener_t* _parent;// for notify parent thread when finished
w_link_t _link; // for listener_t list of clients
};
/*
* Thread that monitors stdin (or some file descriptor) for commands
*/
class stdin_thread_t: public smthread_t {
public:
stdin_thread_t();
~stdin_thread_t();
void run();
// put reply messages here
char reply_buf[thread_reply_buf_size];
private:
sfile_read_hdl_t* _ready; // read handler for stdin
};
#endif /* RPC_THREAD_H */
/* --------------------------------------------------------------- */
/* -- Copyright (c) 1994, 1995 Computer Sciences Department, -- */
/* -- University of Wisconsin-Madison, subject to the terms -- */
/* -- and conditions given in the file COPYRIGHT. All Rights -- */
/* -- Reserved. -- */
/* --------------------------------------------------------------- */
#define RPC_THREAD_C
#include "ShoreConfig.h"
#include <unistd.h>
#include <rpc/rpc.h>
#include "sm_vas.h"
#include "grid_basics.h"
#define RPC_SVC
#include "msg.h"
#include "grid.h"
#include "command.h"
#include "command_server.h"
#include "rpc_thread.h"
#ifdef __GNUG__
// This file uses the following templates. For GCC we must
// explicitly instantiate them.
template class w_list_t<client_t>;
template class w_list_i<client_t>;
#endif
scond_t cleanup("cleanup.s");
smutex_t cleanup_mutex("cleanup.m");
listener_t::listener_t(int fd) :
smthread_t(t_regular, /* regular priority */
false, /* will run ASAP */
false, /* will not delete itself when done */
"listener"), /* thread name */
_fd(fd),
_clients(client_t::link_offset()),
_cleaner_thread(0)
{
_cleaner_thread = new cleaner_t(this);
if(!_cleaner_thread) {
cerr << "cannot fork cleaner thread" <<endl;
::exit(1);
}
W_COERCE(_cleaner_thread->fork());
}
listener_t::~listener_t()
{
/* by the time we get here, client threads
* should have been destroyed
*/
W_COERCE(_clients_mutex.acquire(WAIT_FOREVER));
assert(_clients.is_empty());
_clients_mutex.release();
cout << "listener exiting" << endl;
_cleaner_thread->destroy();
W_COERCE(_cleaner_thread->wait());
delete _cleaner_thread;
_cleaner_thread = 0;
}
void
listener_t::shutdown()
{
// deactivate the file handler
// prevents future connect requests from being accepted
_ready->shutdown();
}
/*
* The real work of the listener thread is done here.
* This method loops waiting for connections and fork a thread
* for each connection (keeping a list of the threads).
* After shutdown() is called no more connections are accepted and
* and the code waits for all clients to end
*/
void
listener_t::run()
{
rc_t rc;
cerr << "creating file handler for listener socket" << endl;
_ready = new sfile_read_hdl_t(_fd);
if(!_ready) {
cerr << "Error: Out of Memory" << endl;
::exit(1);
}
while (1) {
fd_set fds;
FD_ZERO(&fds);
FD_SET(_fd, &fds);
// wait for a connect request
rc = _ready->wait(WAIT_FOREVER);
if (!rc) {
cerr << "listener detects connection" << endl;
// have the rpc library do the accept.
// it doesn't process any msgs.
assert(FD_ISSET(_fd, &svc_fdset));
svc_getreqset(&fds); // get the connect request and
// call its handler
if (! (FD_ISSET(_fd, &svc_fdset))) {
// The socket we're listening on was closed!!
// This is an error case.
cerr << "listener: RPC removed fd "
<< _fd << " from the set" << endl;
break;
}
} else {
// someone did a _ready->shutdown()
// server must be shutting down, so exit.
break;
}
/*
* At this point, RPC has accepted a new connection
* from a client. We need to find out the socket being
* used to that we can fork a thread to service it.
*/
assert(FD_ISSET(_fd, &svc_fdset));
int client_sock = -1;
{
int i;
// loop over all file descriptors
const max_open_fds = sysconf(_SC_OPEN_MAX);
for (i = 0; i < max_open_fds; i++) {
/*
* If this file descriptor is serviced by RPC
* AND there is no active file descriptor handler
* for it, then this must be the new connection
*/
if (FD_ISSET(i, &svc_fdset) &&
!sfile_hdl_base_t::is_active(i)) {
client_sock = i;
break; // the for loop
}
}
}
// we must have found the connection
assert(client_sock>0);
/*
* Fork a thread to process requests from the new client
*/
{
// fork a thread to get requests off the socket
client_t* c;
c = new client_t(client_sock, this);
if(!c ) {
cerr << "Error: could not fork client thread." <<endl;
::exit(1);
}
W_COERCE(c->fork());
cerr << "Forked thread to handle client" << endl;
// put new thread on list of clients
W_COERCE(_clients_mutex.acquire(WAIT_FOREVER));
_clients.append(c);
_clients_mutex.release();
}
}
_cleaner_thread->kick();
/*
* Wait for all client threads to end
*/
cout << "listener waiting for all clients to end ..." << endl;
// must get mutex on the list before checking it
W_COERCE(_clients_mutex.acquire(WAIT_FOREVER));
while(!_clients.is_empty()) {
// this code waits free's the mutex protecting the list
// and waits for _clients_empty to be signaled
W_COERCE(_clients_empty.wait(_clients_mutex));
}
_clients_mutex.release();
cout << "listener exiting" << endl;
delete _ready;
}
/*
* This is a "call-back" function called by children (client_t)
* of listener.
*/
void
listener_t::child_is_done(w_link_t&
#ifdef OLDWAY
child
#endif
)
{
// new way: just let child go to defunct state
// and let cleaner_t remove the defunct threads and delete them
_cleaner_thread->kick();
#ifdef OLDWAY
// must get mutex on the list before changing it
W_COERCE(_clients_mutex.acquire(WAIT_FOREVER));
// remove the child from the list
child.detach();
if (_clients.is_empty()) {
// tell the listener thread that there are no more children
_clients_empty.signal();
}
_clients_mutex.release();
#endif
}
/********************************************************************
Implementation of client_t:
a thread class that manages a client connection and
processes RPCs
********************************************************************/
client_t::client_t(int fd, listener_t* parent)
: smthread_t(t_regular, /* regular priority */
false, /* will run ASAP */
false, /* will not delete itself when done */
"client"), /* thread name */
_fd(fd), _parent(parent)
{
}
client_t::~client_t()
{
}
void
client_t::run()
{
cerr << "New client thread "
<< smthread_t::me()->id
<< " is running" << endl;
_ready = new sfile_read_hdl_t(_fd);
if (!_ready) {
cerr << "Error: Out of Memory" << endl;
::exit(1);
}
cerr << "client thread "
<< smthread_t::me()->id
<< " has read handler for fd "
<< _fd << endl;
// start up C++ side of RPCs processing
command_server = new command_server_t;
if (command_server == 0) {
cerr << "Error: Out of memory" << endl;
::exit(1);
}
cerr << "client thread "
<< smthread_t::me()->id
<< " has command server" << endl;
fd_set fds;
FD_ZERO(&fds);
FD_SET(_fd, &fds);
rc_t rc;
while(1) {
rc = _ready->wait(WAIT_FOREVER);
if (!rc) {
/**********************************************************
* The essence of RPC handling is in svc_getreqset, which
* calls the right RPC stub. The RPC stub will then
* call the corresponding command_server_t method
**********************************************************/
svc_getreqset(&fds); // get the request and call its handler
if (! (FD_ISSET(_fd, &svc_fdset))) {
cerr << " client on fd "
<< _fd << " hung up" << endl;
break;
}
} else {
// someone called _ready->shutdown().
cerr << "exiting client thread for fd: " << _fd << endl;
break;
}
}
assert(sfile_read_hdl_t::is_active(_fd));
delete _ready;
cerr << "Thread "
<< smthread_t::me()->id
<< " deleted read file handler " <<endl;
_ready = 0;
assert(!sfile_read_hdl_t::is_active(_fd));
cerr << "Thread "
<< smthread_t::me()->id
<< " deleted server " <<endl;
delete command_server;
command_server = 0;
// tell the listener thread we are done
_parent->child_is_done(_link);
}
/********************************************************************
Implementation of stdin_thread_t:
a thread class that manages input from the terminal
********************************************************************/
stdin_thread_t::stdin_thread_t() :
smthread_t(t_regular, /* regular priority */
false, /* will run ASAP */
false, /* will not delete itself when done */
"stdin") /* thread name */
{
}
stdin_thread_t::~stdin_thread_t()
{
}
void
stdin_thread_t::run()
{
cerr << "Command thread is running" << endl;
char line_buf[256];
char* line;
bool quit = false;
rc_t rc;
// start a command server
command_server_t cmd_server;
_ready = new sfile_read_hdl_t(0); // handle stdin
if (!_ready) {
cerr << "Error: Out of Memory" << endl;
::exit(1);
}
while (1) {
cout << "Server> " ; cout.flush();
rc = _ready->wait(WAIT_FOREVER);
if(!rc) {
//cerr << "stdin ready" << endl;
line = fgets(line_buf, sizeof(line_buf)-1, stdin);
if (line == 0) {
// end-of-file
break;
}
cmd_server.parse_command(line_buf, quit);
if (quit) {
// quit command was entered
break;
}
} else {
// someone called _ready->shutdown().
cerr << "exiting command thread " << endl;
break;
}
}
assert(sfile_read_hdl_t::is_active(0));
delete _ready;
_ready = 0;
assert(!sfile_read_hdl_t::is_active(0));
cout << "Shutting down command thread" << endl;
}
cleaner_t::cleaner_t(listener_t *_l)
: smthread_t( smthread_t::t_regular, // priority
false, // block_immediate
false, // auto_delete
"cleaner_t", //thread name
WAIT_FOREVER // don't block on locks
)
{
_quit = false;
_listener = _l;
}
void
cleaner_t::kick()
{
cleanup.broadcast();
}
void
cleaner_t::destroy()
{
_quit = true;
cleanup.broadcast();
}
void
cleaner_t::run()
{
while(1) {
if(_quit) {
return;
}
// wait on condition
w_rc_t e;
W_COERCE(cleanup_mutex.acquire(WAIT_FOREVER));
if(e=cleanup.wait(cleanup_mutex)) {
cerr << e << endl; assert(0);
}
cleanup_mutex.release();
W_COERCE(_listener->_clients_mutex.acquire(WAIT_FOREVER));
{
// iterate over list of client_t
w_list_i<client_t> i(_listener->_clients);
client_t* c;
while( (c=i.next()) ) {
if(c->status() == t_defunct) {
c->_link.detach();
}
}
}
if (_listener->_clients.is_empty()) {
// tell the listener thread that there are no more children
_listener->_clients_empty.signal();
}
_listener->_clients_mutex.release();
}
}
/* --------------------------------------------------------------- */
/* -- Copyright (c) 1994, 1995 Computer Sciences Department, -- */
/* -- University of Wisconsin-Madison, subject to the terms -- */
/* -- and conditions given in the file COPYRIGHT. All Rights -- */
/* -- Reserved. -- */
/* --------------------------------------------------------------- */
/*
* This file implements the main() code for the grid client program
*/
#include "ShoreConfig.h"
#include <stream.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <rpc/rpc.h>
#ifdef SOLARIS2
#include <rpc/clnt_soc.h>
#endif
#include <memory.h>
// The client program does not need all of the SSM stuff, so
// instead of sm_vas.h, only sm_app.h is included
#include "sm_app.h"
#include "grid_basics.h"
#define RPC_CLNT /* so rpc prototypes are included */
#include "msg.h"
#include "grid.h"
#include "command.h"
#include "command_client.h"
// shorten error code type name
typedef w_rc_t rc_t;
// this is implemented in options.C
w_rc_t init_config_options(option_group_t& options,
const char* prog_type,
int& argc, char** argv);
// pointer to RPC service this client uses
CLIENT* client = 0;
/*
* connect_to_server connects to the server on machine
* "hostname" at port "port". If connection succeeds,
* true is returned. Otherwise a message is printed to
* cerr and false is returned.
*/
bool
connect_to_server(const char* hostname, int port)
{
/*
* If the server was registered with the port mapper,
* then this function would just call the RPC function
* clnt_create(). Since the server is not, but is instead
* listening on "port" then we need to use the RPC
* function clnttcp_create().
*
* The RPC package shipped with Shore has a clnt_create_port
* function that allows the port to be specified, eliminating
* the need for most of the code below.
*/
struct sockaddr_in saddr; // server is located at this address
struct hostent* h; // server host information
int sock; // socket for connection
h = gethostbyname(hostname);
if (h == NULL) {
cerr << "Error: machine: " << hostname << " is unkown" << endl;
return false;
}
if (h->h_addrtype != AF_INET) {
cerr << "Error: machine " << hostname << " does not have an internet address" << endl;
return false;
}
// fill the socket address with host address information
// see inet(4) for more information
saddr.sin_family = h->h_addrtype;
memset(saddr.sin_zero, 0, sizeof(saddr.sin_zero));
memcpy( (char*)&saddr.sin_addr, h->h_addr, h->h_length);
// set the port to connect to (using 0 would use the portmapper)
saddr.sin_port = htons(port);
// connect with the server
cerr << "attempting server connection" << endl;
sock = RPC_ANYSOCK; // connect using a new socket
client = clnttcp_create(&saddr, GRID, GRIDVERS, &sock, 0, 0);
if (client == 0) {
cerr << "Error: clnttcp_create() could not connect to server" << endl;
cerr << " server may not be running" << endl;
// print RCP error message
clnt_pcreateerror(hostname);
return false;
}
/*
* Set timeout if rpc's do not return in 30 seconds.
* Rpc's may block at the server while waiting for
* locks, so this may need to be increased.
*
* Note RPC library shipped with Shore allows the
* use of CLRMV_TIMEOUT to completely remove the timeout.
*/
struct timeval tv;
tv.tv_sec = 30;
tv.tv_usec = 0;
if (!clnt_control(client, CLSET_TIMEOUT, (char *) &tv)) {
cerr << "Error: could not set client timeout" << endl;
return false;
}
return true;
}
void
disconnect_from_server()
{
if (client) clnt_destroy(client);
}
void
process_user_commands()
{
command_client_t cmd_client(client);
char line_buf[256];
char* line;
bool quit = false;
cout << "Client ready." << endl;
while(!quit) {
cout << "client> " << flush;
line = fgets(line_buf, sizeof(line_buf)-1, stdin);
if (line == 0) {
// end of file
break;
}
cmd_client.parse_command(line_buf, quit);
}
}
void
usage(option_group_t& options)
{
cerr << "Usage: client [options]" << endl;
cerr << "Valid options are: " << endl;
options.print_usage(TRUE, cerr);
}
int
main(int argc, char* argv[])
{
cout << "processing configuration options ..." << endl;
// pointers to options we will create for the grid server program
option_t* opt_server_host = 0;
option_t* opt_connect_port = 0;
const option_level_cnt = 3;
option_group_t options(option_level_cnt);
W_COERCE(options.add_option("connect_port", "1024 < integer < 65535",
"1234", "port for connecting to grid server",
false, option_t::set_value_long,
opt_connect_port));
W_COERCE(options.add_option("server_host", "host address",
"localhost", "address of host running server",
false, option_t::set_value_charstr,
opt_server_host));
if (init_config_options(options, "client", argc, argv)) {
usage(options);
exit(1);
}
// there should not be any other command line arguments
if (argc > 1) {
usage(options);
exit(1);
}
int port = strtol(opt_connect_port->value(), 0, 0);
cout << "trying to connect to server at port " << port<< endl;
if (!connect_to_server(opt_server_host->value(), port)) {
cerr << "Shutting down due to connection failure" << endl;
exit(1);
}
process_user_commands();
disconnect_from_server();
cout << "Finished!" << endl;
return 0;
}
/* --------------------------------------------------------------- */
/* -- Copyright (c) 1994, 1995 Computer Sciences Department, -- */
/* -- University of Wisconsin-Madison, subject to the terms -- */
/* -- and conditions given in the file COPYRIGHT. All Rights -- */
/* -- Reserved. -- */
/* --------------------------------------------------------------- */
#ifndef COMMAND_CLIENT_H
#define COMMAND_CLIENT_H
/*
* Client command processing class
*/
class command_client_t : public command_base_t {
public:
command_client_t(CLIENT*);
virtual cmd_err_t commit_transaction();
virtual cmd_err_t abort_transaction();
virtual cmd_err_t clear_grid();
virtual cmd_err_t print_grid(grid_display_t& rows);
virtual cmd_err_t add_item(const char* name, int x, int y);
virtual cmd_err_t remove_item(const char* name);
virtual cmd_err_t move_item(const char* name, int x, int y);
virtual cmd_err_t location_of(const char* name, int& x, int& y);
virtual cmd_err_t spatial_query(const nbox_t& box, spatial_result_t& result);
private:
CLIENT* cl;
};
#endif /* COMMAND_CLIENT_H */
/* --------------------------------------------------------------- */
/* -- Copyright (c) 1994, 1995 Computer Sciences Department, -- */
/* -- University of Wisconsin-Madison, subject to the terms -- */
/* -- and conditions given in the file COPYRIGHT. All Rights -- */
/* -- Reserved. -- */
/* --------------------------------------------------------------- */
/*
* This file implements the main() code for the grid client program
*/
#include "ShoreConfig.h"
#include <stream.h>
#include <string.h>
#include <rpc/rpc.h>
// include stuff needed for SM applications (clients)
#include "sm_app.h"
#include "grid_basics.h"
#define RPC_CLNT /* so rpc prototypes are included */
#include "msg.h"
#include "grid.h"
#include "command.h"
#include "command_client.h"
#define DO_RPC(rpc, reply) \
reply = rpc; \
if (reply == 0) { \
return clnt_sperror(cl, ""); \
} else if (reply->error_msg[0] != 0) { \
return reply->error_msg; \
}
command_client_t::command_client_t(CLIENT* client)
: cl(client)
{
}
cmd_err_t
command_client_t::commit_transaction()
{
error_reply* reply;
DO_RPC(commit_transaction_rpc_1(0, cl), reply);
return 0; /* success */
}
cmd_err_t
command_client_t::abort_transaction()
{
error_reply* reply;
DO_RPC(abort_transaction_rpc_1(0, cl), reply);
return 0; /* success */
}
cmd_err_t
command_client_t::clear_grid()
{
error_reply* reply;
DO_RPC(clear_grid_rpc_1(0, cl), reply);
return 0; /* success */
}
cmd_err_t
command_client_t::print_grid(grid_display_t& display)
{
print_grid_reply* reply;
DO_RPC(print_grid_rpc_1(0, cl), reply);
display = reply->display;
return 0; /* success */
}
cmd_err_t
command_client_t::add_item(const char* name, int x, int y)
{
error_reply* reply;
add_item_arg arg;
strncpy(arg.name, name, sizeof(arg.name)-1);
arg.x = x;
arg.y = y;
DO_RPC(add_item_rpc_1(&arg, cl), reply);
return 0; /* success */
}
cmd_err_t
command_client_t::remove_item(const char* name)
{
error_reply* reply;
remove_item_arg arg;
strncpy(arg.name, name, sizeof(arg.name)-1);
DO_RPC(remove_item_rpc_1(&arg, cl), reply);
return 0; /* success */
}
cmd_err_t
command_client_t::move_item(const char* name, int x, int y)
{
error_reply* reply;
add_item_arg arg;
arg.x = x;
arg.y = y;
strncpy(arg.name, name, sizeof(arg.name)-1);
DO_RPC(move_item_rpc_1(&arg, cl), reply);
return 0; /* success */
}
cmd_err_t
command_client_t::location_of(const char* name, int& x, int& y)
{
location_reply* reply;
location_arg arg;
strncpy(arg.name, name, sizeof(arg.name)-1);
DO_RPC(location_of_rpc_1(&arg, cl), reply);
x = reply->x;
y = reply->y;
return 0; /* success */
}
cmd_err_t
command_client_t::spatial_query(const nbox_t& box, spatial_result_t& result)
{
spatial_reply* reply;
spatial_arg arg;
arg.x_low = box.bound(0);
arg.y_low = box.bound(1);
arg.x_hi = box.bound(0+box.dimension());
arg.y_hi = box.bound(1+box.dimension());
DO_RPC(spatial_rpc_1(&arg, cl), reply);
result = reply->result;
return 0; /* success */
}