mirror of
https://github.com/EvernodeXRPL/hpcore.git
synced 2026-04-29 15:37:59 +00:00
Integrated default app bill verification for user inputs. (#72)
Initial appbill implementation from @codetsunami integrated to hpcore. #47
This commit is contained in:
@@ -123,12 +123,19 @@ target_link_libraries(hpstatemon
|
||||
libfuse3.so.3
|
||||
)
|
||||
|
||||
add_executable(appbill
|
||||
src/bill/appbill.cpp
|
||||
)
|
||||
|
||||
add_dependencies(hpcore
|
||||
hpstatemon
|
||||
appbill
|
||||
)
|
||||
|
||||
# add_custom_command(TARGET hpcore POST_BUILD
|
||||
# COMMAND strip ./build/hpcore
|
||||
# COMMAND strip ./build/hpstatemon
|
||||
# COMMAND strip ./build/appbill
|
||||
# )
|
||||
|
||||
target_precompile_headers(hpsupport PUBLIC src/pchheader.hpp)
|
||||
|
||||
@@ -48,6 +48,8 @@ do
|
||||
node -p "JSON.stringify({...require('./tmp.json'), \
|
||||
binary: '/usr/local/bin/node', \
|
||||
binargs: './bin/contract.js', \
|
||||
appbill: 'appbill', \
|
||||
appbillargs: '', \
|
||||
peerport: ${peerport}, \
|
||||
pubport: ${pubport}, \
|
||||
roundtime: 1000, \
|
||||
@@ -61,9 +63,10 @@ do
|
||||
-subj "/C=AU/ST=ST/L=L/O=O/OU=OU/CN=localhost/emailAddress=hpnode${n}@example" > /dev/null 2>&1
|
||||
popd > /dev/null 2>&1
|
||||
|
||||
# Copy the contract executable.
|
||||
# Copy the contract executable and appbill.
|
||||
mkdir ./node$n/bin
|
||||
cp ../examples/echocontract/contract.js ./node$n/bin/contract.js
|
||||
cp ../build/appbill ./node$n/bin/
|
||||
done
|
||||
|
||||
# Function to generate JSON array string while skiping a given index.
|
||||
@@ -107,6 +110,23 @@ do
|
||||
popd > /dev/null 2>&1
|
||||
done
|
||||
|
||||
# Setup initial state data for all nodes but one.
|
||||
for (( i=1; i<=$ncount; i++ ))
|
||||
do
|
||||
|
||||
sudo mkdir -p ./node$i/statehist/0/data/ > /dev/null 2>&1
|
||||
|
||||
# Load credit balance for user for testing purposes.
|
||||
pushd ./node$i/statehist/0/data/ > /dev/null 2>&1
|
||||
>appbill.table
|
||||
../../../../../build/appbill --credit "705bf26354ee4c63c0e5d5d883c07cefc3196d049bd3825f827eb3bc23ead035" 10000
|
||||
popd > /dev/null 2>&1
|
||||
|
||||
# Copy any more initial state files for testing.
|
||||
#cp ~/my_big_file ~/hpcore/hpcluster/node$i/statehist/0/data/
|
||||
|
||||
done
|
||||
|
||||
popd > /dev/null 2>&1
|
||||
|
||||
# Create docker virtual network named 'hpnet'
|
||||
@@ -116,4 +136,4 @@ docker network create --driver bridge hpnet > /dev/null 2>&1
|
||||
echo "Cluster generated at ${clusterloc}"
|
||||
echo "Use \"./cluster-start.sh <nodeid>\" to run each node."
|
||||
|
||||
exit 0
|
||||
exit 0
|
||||
|
||||
@@ -6,7 +6,7 @@ const fs = require('fs')
|
||||
//console.log("===Sample contract started===");
|
||||
//console.log("Contract args received from hp: " + input);
|
||||
|
||||
let hpargs = JSON.parse( fs.readFileSync(0, 'utf8'));
|
||||
let hpargs = JSON.parse(fs.readFileSync(0, 'utf8'));
|
||||
|
||||
// We just save execution args as an example state file change.
|
||||
fs.appendFileSync("state/exects.txt", "ts:" + hpargs.ts + "\n");
|
||||
@@ -36,4 +36,4 @@ if (hpinput.length > 0) {
|
||||
fs.writeSync(hpargs.hpfd[1], "Echoing: " + hpinput);
|
||||
}
|
||||
|
||||
//console.log("===Sample contract ended===");
|
||||
//console.log("===Sample contract ended===");
|
||||
|
||||
3
examples/hpclient/.gitignore
vendored
3
examples/hpclient/.gitignore
vendored
@@ -1,2 +1 @@
|
||||
node_modules/**
|
||||
.hp_client_keys
|
||||
node_modules/**
|
||||
1
examples/hpclient/.hp_client_keys
Normal file
1
examples/hpclient/.hp_client_keys
Normal file
@@ -0,0 +1 @@
|
||||
{"publicKey":"705bf26354ee4c63c0e5d5d883c07cefc3196d049bd3825f827eb3bc23ead035","privateKey":"07ce6f7d6f0da38d5956ecc4ea1d18de77fc8bded089eb52199a46ffe2098c88705bf26354ee4c63c0e5d5d883c07cefc3196d049bd3825f827eb3bc23ead035","keyType":"ed25519"}
|
||||
10
reset.sh
10
reset.sh
@@ -2,12 +2,16 @@
|
||||
|
||||
nodes=3
|
||||
sudo ./cluster-create.sh $nodes
|
||||
|
||||
WD=`pwd`
|
||||
# Setup initial state data for all nodes but one.
|
||||
for (( i=1; i<$nodes; i++ ))
|
||||
do
|
||||
|
||||
|
||||
sudo mkdir -p ~/hpcore/hpcluster/node$i/statehist/0/data/
|
||||
pushd ~/hpcore/hpcluster/node$i/statehist/0/data/
|
||||
>appbill.table
|
||||
$WD/build/appbill --credit 705bf26354ee4c63c0e5d5d883c07cefc3196d049bd3825f827eb3bc23ead035 10000
|
||||
popd
|
||||
#sudo cp -r ~/Downloads/big.mkv ~/hpcore/hpcluster/node$i/statehist/0/data/
|
||||
|
||||
done
|
||||
done
|
||||
|
||||
705
src/bill/appbill.cpp
Normal file
705
src/bill/appbill.cpp
Normal file
@@ -0,0 +1,705 @@
|
||||
/*
|
||||
App bill default implementation 1
|
||||
MSB of return value is reserved for appbill error
|
||||
bits 1-8 indicate whether or not public keys 0-6 passed appbill check
|
||||
|
||||
(to be completed)
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/uio.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define DEBUG 0
|
||||
#define KEY_SIZE 32
|
||||
#define RECORD_SIZE 64
|
||||
|
||||
#define FILE_BUFFER_SIZE (64*1024*1024) // this will move 0xffff entries at a time
|
||||
|
||||
#define MIN(a,b) (((a)<(b))?(a):(b))
|
||||
#define MAX(a,b) (((a)>(b))?(a):(b))
|
||||
|
||||
#define TABLE_FILE "appbill.table"
|
||||
#define TABLE_FILE_2 "./state/appbill.table" // if TABLE_FILE can't be found try here
|
||||
|
||||
uint64_t new_balance(uint64_t balance, int64_t to_credit) {
|
||||
if (to_credit < 0 && -to_credit > balance) {
|
||||
// catch the wrap around
|
||||
balance = 0;
|
||||
} else if (to_credit > 0 && to_credit + balance < balance) {
|
||||
// and here as well
|
||||
balance = (uint64_t)-1;
|
||||
} else {
|
||||
// normal crediting
|
||||
balance += to_credit;
|
||||
}
|
||||
return balance;
|
||||
}
|
||||
|
||||
void print_hex(uint8_t* data, int len) {
|
||||
for (int c = 0; c < len; ++c)
|
||||
printf("%02hhx", data[c]);
|
||||
}
|
||||
|
||||
int compar (const void* p1, const void* p2) {
|
||||
for (uint8_t* c1 = (uint8_t*)p1, * c2 = (uint8_t*)p2; c1 - (uint8_t*)p1 < KEY_SIZE; ++c1, ++c2)
|
||||
if (*c1 < *c2) return -1;
|
||||
else if (*c1 > *c2) return +1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int correct_for_ed_keys(int argc, char** argv, int incr, int offset) {
|
||||
// correct for ed keys
|
||||
for (int i = 0; i < argc; i += incr) {
|
||||
int len = strlen(argv[i + offset]);
|
||||
if (len == KEY_SIZE*2 + 2 && (argv[i][0] == 'e' || argv[i][0] == 'E') && (argv[i][1] == 'd' || argv[i][1] == 'D'))
|
||||
argv[i]+=2;
|
||||
else if (len != KEY_SIZE*2) {
|
||||
fprintf(stderr, "appbill received an invalid key %s, expected len=%d actual len=%d\n", argv[i], KEY_SIZE*2, len);
|
||||
return 128;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void key_from_hex(uint8_t* key_in, uint8_t* key_out) {
|
||||
for (int c = 0; c < 32; ++c) {
|
||||
uint8_t hn = tolower(key_in[c*2]);
|
||||
uint8_t ln = tolower(key_in[c*2+1]);
|
||||
hn = ( hn >= 'a' ? 10 + (hn - 'a') : hn - '0');
|
||||
ln = ( ln >= 'a' ? 10 + (ln - 'a') : ln - '0');
|
||||
key_out[c] = (hn * 16) + ln;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t uint64_from_bytes(uint8_t* data) {
|
||||
return
|
||||
(((uint64_t)(data[0]))<<56) +
|
||||
(((uint64_t)(data[1]))<<48) +
|
||||
(((uint64_t)(data[2]))<<40) +
|
||||
(((uint64_t)(data[3]))<<32) +
|
||||
(((uint64_t)(data[4]))<<24) +
|
||||
(((uint64_t)(data[5]))<<16) +
|
||||
(((uint64_t)(data[6]))<<8) +
|
||||
(((uint64_t)(data[7])));
|
||||
}
|
||||
|
||||
|
||||
void uint64_to_bytes(uint8_t* dest, uint64_t x) {
|
||||
for (int j = 0; j < 8; j++) {
|
||||
*(dest + (7-j)) = x & 0xff;
|
||||
x >>= 8;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// returns 1 if found and populates entry_out, 0 if not found
|
||||
int binary_file_search(FILE* f, uint8_t* key, uint8_t* entry_out, uint64_t* balance_out, size_t* recordno_out, int* error_out) {
|
||||
|
||||
*error_out = 0;
|
||||
*balance_out = 0;
|
||||
|
||||
// find file size
|
||||
fseek(f, (size_t)0, SEEK_END);
|
||||
size_t tablesize = ftell(f);
|
||||
int recordcount = tablesize / RECORD_SIZE;
|
||||
|
||||
size_t record = recordcount/2;
|
||||
size_t search_size = (recordcount == 1 ? 1 : record);
|
||||
|
||||
|
||||
|
||||
uint8_t entry_array[RECORD_SIZE*2];
|
||||
uint8_t* entry = entry_array + RECORD_SIZE;
|
||||
uint8_t* prev_entry = entry_array;
|
||||
|
||||
|
||||
while (search_size) {
|
||||
|
||||
if (record == 0) {
|
||||
// special case, no previous record will be available
|
||||
fseek(f, 0, SEEK_SET);
|
||||
int r = fread(entry, 1, RECORD_SIZE, f);
|
||||
if (r != RECORD_SIZE) {
|
||||
fprintf(stderr, "failed to read %d bytes\n", RECORD_SIZE);
|
||||
*error_out = 128;
|
||||
return 0;
|
||||
}
|
||||
memset(prev_entry, 0, RECORD_SIZE);
|
||||
} else {
|
||||
fseek(f, (record - 1) * RECORD_SIZE, SEEK_SET);
|
||||
int r = fread(entry_array, 1, RECORD_SIZE * 2, f);
|
||||
if (r != RECORD_SIZE*2) {
|
||||
fprintf(stderr, "failed to read %d bytes\n", RECORD_SIZE*2);
|
||||
*error_out = 128;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int search_direction = compar(entry, key);
|
||||
int check_prev = compar(prev_entry, key);
|
||||
|
||||
if (DEBUG) {
|
||||
printf("prevrec: %lu\tkey: ", record-1);
|
||||
print_hex(prev_entry, RECORD_SIZE);
|
||||
printf("\tsearch dir: %d\n", check_prev);
|
||||
printf("record: %lu\tkey: ", record);
|
||||
print_hex(entry, RECORD_SIZE);
|
||||
printf("\tsearch dir: %d\n", search_direction);
|
||||
}
|
||||
|
||||
|
||||
if (search_direction == 0) {
|
||||
// get the balance
|
||||
*balance_out = uint64_from_bytes(entry+32);
|
||||
if (DEBUG) printf("entry found at record %lu with balance=%lu\n", record, *balance_out);
|
||||
if (entry_out)
|
||||
for (int i = 0; i < RECORD_SIZE; ++i)
|
||||
entry_out[i] = entry[i];
|
||||
*recordno_out = record;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (check_prev == 0 && record != 0) {
|
||||
// get the balance
|
||||
*balance_out = uint64_from_bytes(prev_entry+32);
|
||||
if (DEBUG) printf("entry found at record %lu with balance=%lu\n", record-1, *balance_out);
|
||||
if (entry_out)
|
||||
for (int i = 0; i < RECORD_SIZE; ++i)
|
||||
entry_out[i] = prev_entry[i];
|
||||
*recordno_out = record-1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (search_direction != check_prev ) {
|
||||
// record doesn't exist, it would go between these two records if it did
|
||||
if (DEBUG) printf("record doesn't exist, would go between\n");
|
||||
*recordno_out = record;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
search_size /= 2;
|
||||
|
||||
if (search_size < 1) search_size = 1;
|
||||
|
||||
if (search_direction > 0) {
|
||||
record -= search_size;
|
||||
} else {
|
||||
record += search_size;
|
||||
}
|
||||
|
||||
if (DEBUG) printf("search size: %lu, current record: %lu, check_prev: %d, dir: %d\n", search_size, record, check_prev, search_direction);
|
||||
|
||||
if (record < 0 || record >= recordcount) {
|
||||
if (DEBUG)
|
||||
fprintf(stderr, "could not find key record: %lu, recordcount: %d\n", record, recordcount);
|
||||
if (entry_out)
|
||||
for (int i = 0; i < RECORD_SIZE; ++i)
|
||||
entry_out[i] = entry[i];
|
||||
*recordno_out = record;
|
||||
*balance_out = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
*recordno_out = record;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// inserts above recordno
|
||||
// warning: expensive, must copy remaining chunk of file down
|
||||
int insert_record(FILE* f, uint8_t* entry, size_t recordno) {
|
||||
|
||||
static uint8_t* file_buffer = 0; // we're going to reuse this piece of memory until appbill closes so just alloc once
|
||||
|
||||
if (!file_buffer)
|
||||
file_buffer = (uint8_t*)malloc(MAX(FILE_BUFFER_SIZE, RECORD_SIZE));
|
||||
|
||||
if (DEBUG) printf("insert_record called with recno=%lu\n", recordno);
|
||||
//uint8_t* buffer[FILE_BUFFER_SIZE];
|
||||
|
||||
size_t offset = recordno * RECORD_SIZE;
|
||||
|
||||
fseek(f, (size_t)0, SEEK_END);
|
||||
size_t size = ftell(f);
|
||||
|
||||
// inserting 64 bytes at offset
|
||||
// we need to first compute the short move at the end
|
||||
|
||||
long long tomove = size - offset;
|
||||
if (tomove <= 0) {
|
||||
// write the record at the end of the file
|
||||
if (DEBUG) printf("new write\n");
|
||||
return fwrite(entry, RECORD_SIZE, 1, f);
|
||||
} else {
|
||||
size_t endpiece = tomove % FILE_BUFFER_SIZE;
|
||||
|
||||
if (DEBUG) printf("endpiece %lu\n", endpiece);
|
||||
|
||||
// the endpiece is always moved, some times there are also no further pieces to move
|
||||
fseek(f, size - endpiece, SEEK_SET);
|
||||
fread(file_buffer, 1, endpiece, f);
|
||||
fseek(f, size - endpiece + RECORD_SIZE, SEEK_SET);
|
||||
fwrite(file_buffer, 1, endpiece, f);
|
||||
|
||||
size_t cursor = size - endpiece;
|
||||
|
||||
tomove -= endpiece;
|
||||
|
||||
// now if there are any other pieces to move along we can move them
|
||||
if (size - endpiece >= FILE_BUFFER_SIZE)
|
||||
for (size_t cursor = size - endpiece - FILE_BUFFER_SIZE; cursor > offset; cursor -= FILE_BUFFER_SIZE) {
|
||||
if (DEBUG) printf("moving %d sized piece at %lu to %lu - size: %lu - offset: %lu\n", FILE_BUFFER_SIZE, cursor, cursor + RECORD_SIZE, size, offset);
|
||||
fseek(f, cursor, SEEK_SET);
|
||||
fread(file_buffer, 1, FILE_BUFFER_SIZE, f);
|
||||
fseek(f, cursor + RECORD_SIZE, SEEK_SET);
|
||||
fwrite(file_buffer, 1, FILE_BUFFER_SIZE, f);
|
||||
}
|
||||
|
||||
// not sure why we need to move this last row down, something is slightly wrong with the math above?
|
||||
fseek(f, offset, SEEK_SET);
|
||||
fread(file_buffer, 1, RECORD_SIZE, f);
|
||||
fseek(f, offset + RECORD_SIZE, SEEK_SET);
|
||||
fwrite(file_buffer, 1, RECORD_SIZE, f);
|
||||
|
||||
// finally it's safe to emplace our data
|
||||
fseek(f, offset, SEEK_SET);
|
||||
return fwrite(entry, 1, RECORD_SIZE, f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int valid_hex(char* hex, int len) {
|
||||
char* x = hex;
|
||||
for (; (x-hex) < len && *x != '\0' && *x != '\n' && *x >= '0' && (*x <= '9' || *x >= 'a' && *x <= 'f' || *x >= 'A' && *x <= 'F'); ++x);
|
||||
return x-hex == len;
|
||||
}
|
||||
|
||||
int pass_through_mode(int argc, char** argv) {
|
||||
// full argc, argv are in tact in this mode
|
||||
|
||||
if (DEBUG)
|
||||
printf("pass through mode\n");
|
||||
|
||||
int teepipe[2];
|
||||
int error = pipe(teepipe);
|
||||
if (error) {
|
||||
fprintf(stderr, "appbill pass through could not create a pipe for teeing fdlist\n");
|
||||
return 128;
|
||||
}
|
||||
|
||||
FILE* teepipeout = fdopen(teepipe[1], "w");
|
||||
|
||||
// todo: make this all zero copy when someone has time to debug tee and vmsplice readmode
|
||||
// for now we'll just do a dumb read
|
||||
|
||||
FILE* f = fopen(TABLE_FILE, "rb+");
|
||||
if (!f)
|
||||
f = fopen(TABLE_FILE_2, "rb+");
|
||||
|
||||
if (!f) {
|
||||
fprintf(stderr, "could not open %s or %s\n", TABLE_FILE, TABLE_FILE_2);
|
||||
return 128;
|
||||
}
|
||||
|
||||
char buf[1024];
|
||||
int mode = 0;
|
||||
int bytes_read = 0;
|
||||
|
||||
int counter = 0;
|
||||
int toskip = 0;
|
||||
|
||||
uint8_t key[KEY_SIZE];
|
||||
|
||||
do {
|
||||
char c = 0;
|
||||
bytes_read = 0;
|
||||
while ( (c = getc( stdin )) != EOF && c != ',' && c != '{' && c != '}' && c != '[' && c != ']' && c != '\n' && c != ':' && bytes_read < 1023 ) {
|
||||
buf[bytes_read++] = c;
|
||||
putc(c, teepipeout); // make a copy for the next program
|
||||
}
|
||||
if (c != EOF) putc(c, teepipeout); // make a copy for the next program
|
||||
|
||||
if (c == EOF)
|
||||
break;
|
||||
|
||||
if (mode == 2)
|
||||
continue;
|
||||
|
||||
buf[bytes_read] = '\0';
|
||||
|
||||
if (mode == 0 && strcmp("\"usrfd\"", buf) == 0) {
|
||||
mode = 1;
|
||||
continue;
|
||||
} else if ( mode == 1 && c == '}' ) {
|
||||
mode = 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (buf[0] == '\0' || !mode)
|
||||
continue;
|
||||
|
||||
++counter %= 3;
|
||||
|
||||
// this runs if there's an error in the user's public key
|
||||
if (toskip) {
|
||||
toskip--;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (DEBUG)
|
||||
printf("mode=%d counter=%d component `%s`\n", mode, counter%3, buf);
|
||||
|
||||
if (counter == 1) {
|
||||
// this is the user key
|
||||
// remove trailing "
|
||||
if (!buf[strlen(buf)-1] == '"')
|
||||
continue;
|
||||
buf[strlen(buf)-1] = '\0';
|
||||
|
||||
// check the key is valid
|
||||
if (DEBUG)
|
||||
printf("key length: %lu, proper length: %d\n", strlen(buf+3), KEY_SIZE*2);
|
||||
if (DEBUG)
|
||||
printf("hex: `%s`\n", buf+3);
|
||||
if (strlen(buf+3) != KEY_SIZE*2 || !valid_hex(buf+3, KEY_SIZE*2)) {
|
||||
toskip = 2;
|
||||
if (DEBUG)
|
||||
printf("invald public key %s\n", buf+3);
|
||||
continue;
|
||||
}
|
||||
|
||||
key_from_hex((uint8_t*)buf+3, key);
|
||||
if (DEBUG) {
|
||||
printf("parsed key: ");
|
||||
print_hex(key, KEY_SIZE);
|
||||
printf("\n");
|
||||
}
|
||||
} else if (counter == 2) {
|
||||
// this is the user's input fd
|
||||
|
||||
int userfd = 0;
|
||||
if (!sscanf(buf, "%d", &userfd))
|
||||
continue;
|
||||
|
||||
if (DEBUG) printf("mode=2 userfd=%d\n", userfd);
|
||||
|
||||
// there might be some bytes pending on this input, if there are we need to bill for them, one coin per byte
|
||||
|
||||
/*int nbytes = 0;
|
||||
ioctl(userfd, FIONREAD, &nbytes);*/
|
||||
|
||||
int64_t to_bill = 0; // and one coin per round for being connected too // no change that to 0 because otherwise malicious nodes can drain accounts!
|
||||
|
||||
//todo: replace all this rubbish with a properly tested zero copy approach
|
||||
int userpipe[2];
|
||||
pipe(userpipe); //todo: handle possible error condition here
|
||||
FILE* userfile = fdopen(userfd, "r");
|
||||
FILE* newuserfile = fdopen(userpipe[1], "w");
|
||||
|
||||
char x = 0;
|
||||
while ((x = getc(userfile)) != EOF) {
|
||||
if (to_bill < (uint64_t)-1)
|
||||
to_bill++;
|
||||
putc(x, newuserfile);
|
||||
}
|
||||
|
||||
fclose(newuserfile);
|
||||
fclose(userfile);
|
||||
dup2(userpipe[0], userfd);
|
||||
|
||||
|
||||
if (DEBUG)
|
||||
printf("tobill: %lu\n", to_bill);
|
||||
|
||||
// commence billing
|
||||
|
||||
int error = 0;
|
||||
uint64_t balance = 0;
|
||||
size_t recordno = 0;
|
||||
uint8_t entry[64];
|
||||
if (binary_file_search(f, key, entry, &balance, &recordno, &error)) {
|
||||
// key already exists, update it
|
||||
if (DEBUG) printf("writing 64 bytes at record:%lu\n", recordno);
|
||||
fseek(f, recordno*RECORD_SIZE, SEEK_SET);
|
||||
uint64_t balance = uint64_from_bytes(entry+32);
|
||||
balance = new_balance(balance, -to_bill);
|
||||
uint64_to_bytes(entry+32, balance);
|
||||
fwrite(entry, RECORD_SIZE, 1, f);
|
||||
} else {
|
||||
// is user doesn't exist this is an error but we can't do anything about it in passthrough mode so ignore
|
||||
if (DEBUG) {
|
||||
printf("user not found key:");
|
||||
print_hex(key, KEY_SIZE);
|
||||
printf("\n");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
} while (!feof(stdin));
|
||||
|
||||
fflush(teepipeout);
|
||||
|
||||
close(teepipe[1]);
|
||||
dup2(teepipe[0], 0);
|
||||
|
||||
fclose(f);
|
||||
|
||||
execv(argv[1], argv+1);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
int credit_mode(int argc, char** argv) {
|
||||
// argc,v start from first useful arguments
|
||||
if (argc == 0 || argc % 2 == 1) {
|
||||
fprintf(stderr, "appbill credit mode requires args like: public_key amount public key amount\n");
|
||||
return 128;
|
||||
}
|
||||
|
||||
if (correct_for_ed_keys(argc, argv, 2, 0))
|
||||
return 128;
|
||||
|
||||
// sanity check our inputs
|
||||
for (int i = 0; i < argc; i += 2) {
|
||||
for (char* x = argv[i];; ++x) {
|
||||
if ( x - argv[i] == KEY_SIZE*2 && *x != '\0' ) {
|
||||
fprintf(stderr, "appbill was supplied an invalid public key\n");
|
||||
return 128;
|
||||
}
|
||||
|
||||
if (*x >= 'a' && *x <= 'f' || *x >= '0' && *x <= '9' || *x >= 'A' && *x <= 'F')
|
||||
continue;
|
||||
|
||||
if (*x == '\0')
|
||||
break;
|
||||
|
||||
fprintf(stderr, "appbill was supplied an invalid public key (not hex) char=%c\n", *x);
|
||||
return 128;
|
||||
}
|
||||
|
||||
for (char* x = argv[i+1]; *x; ++x)
|
||||
if ( ! (*x >= '0' && *x <= '9' || *x == '-') ) {
|
||||
fprintf(stderr, "appbill was supplied invalid amount to credit, must be decimal integer entry=%s\n", argv[i+1]);
|
||||
return 128;
|
||||
}
|
||||
|
||||
int64_t to_credit = 0;
|
||||
if (!sscanf(argv[i+1], "%ld", &to_credit)) {
|
||||
fprintf(stderr, "appbill was supplied invalid amount to credit, must be decimal integer entry=%s\n", argv[i+1]);
|
||||
return 128;
|
||||
}
|
||||
}
|
||||
|
||||
FILE* f = fopen(TABLE_FILE, "rb+");
|
||||
if (!f)
|
||||
f = fopen(TABLE_FILE_2, "rb+");
|
||||
|
||||
if (!f) {
|
||||
fprintf(stderr, "could not open %s or %s\n", TABLE_FILE, TABLE_FILE_2);
|
||||
return 128;
|
||||
}
|
||||
|
||||
// now the expensive bit
|
||||
for (int i = 0; i < argc; i += 2) {
|
||||
uint8_t key[32];
|
||||
key_from_hex((uint8_t*)argv[i], key);
|
||||
|
||||
int64_t to_credit = 0;
|
||||
if (!sscanf(argv[i+1], "%ld", &to_credit)) // this has been sanity checked above
|
||||
continue;
|
||||
|
||||
int error = 0;
|
||||
uint64_t balance = 0;
|
||||
size_t recordno = 0;
|
||||
uint8_t entry[64];
|
||||
if (binary_file_search(f, key, entry, &balance, &recordno, &error)) {
|
||||
// key already exists, update it
|
||||
if (DEBUG) printf("writing 64 bytes at record:%lu\n", recordno);
|
||||
fseek(f, recordno*RECORD_SIZE, SEEK_SET);
|
||||
uint64_t balance = uint64_from_bytes(entry+32);
|
||||
balance = new_balance(balance, to_credit);
|
||||
uint64_to_bytes(entry+32, balance);
|
||||
fwrite(entry, RECORD_SIZE, 1, f);
|
||||
|
||||
} else {
|
||||
|
||||
if (DEBUG) printf("key needs to be inserted\n");
|
||||
// key doesn't exist, insert it
|
||||
uint8_t new_entry[RECORD_SIZE];
|
||||
for (int i = 0; i < KEY_SIZE; ++i)
|
||||
new_entry[i] = key[i];
|
||||
uint64_t balance = new_balance(0, to_credit);
|
||||
|
||||
uint64_to_bytes(new_entry+32, balance);
|
||||
|
||||
for (int i = KEY_SIZE + 8; i < RECORD_SIZE; ++i)
|
||||
new_entry[i] = 0;
|
||||
|
||||
// get the existing entry
|
||||
fseek(f, recordno * RECORD_SIZE, SEEK_SET);
|
||||
fread(entry, 1, RECORD_SIZE, f);
|
||||
|
||||
int insert_direction = compar(entry, new_entry);
|
||||
|
||||
insert_record(f, new_entry, recordno + (insert_direction < 0 ? 1 : 0));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int check_mode(int argc, char** argv, int print_balances) {
|
||||
|
||||
if (DEBUG)
|
||||
printf("check mode\n");
|
||||
|
||||
if (argc > 14 && !print_balances) {
|
||||
fprintf(stderr, "appbill can only take up to 7 keys at a time\n");
|
||||
return 128;
|
||||
}
|
||||
|
||||
if (correct_for_ed_keys(argc, argv, 2, 0))
|
||||
return 128;
|
||||
|
||||
for (int i = 0; i < argc; i+=2) {
|
||||
// check the pubkey
|
||||
for (char* x = argv[i];; ++x) {
|
||||
if ( x - argv[i] == KEY_SIZE*2 && *x != '\0' ) {
|
||||
fprintf(stderr, "appbill was supplied an invalid public key\n");
|
||||
return 128;
|
||||
}
|
||||
|
||||
if (*x >= 'a' && *x <= 'f' || *x >= '0' && *x <= '9' || *x >= 'A' && *x <= 'F')
|
||||
continue;
|
||||
|
||||
if (*x == '\0')
|
||||
break;
|
||||
|
||||
fprintf(stderr, "appbill was supplied an invalid public key (not hex) char=%c\n", *x);
|
||||
return 128;
|
||||
}
|
||||
|
||||
// check the bytecount
|
||||
for (char* x = argv[i+1]; *x != '\0'; ++x) {
|
||||
if (*x >= '0' && *x <= '9')
|
||||
continue;
|
||||
fprintf(stderr, "appbill was supplied invalid byte count %s\n", argv[i+1]);
|
||||
return 128;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (print_balances)
|
||||
printf("{\n");
|
||||
|
||||
// open app bill table
|
||||
|
||||
FILE* f = fopen(TABLE_FILE, "rb");
|
||||
if (!f)
|
||||
f = fopen(TABLE_FILE_2, "rb");
|
||||
|
||||
if (!f) {
|
||||
fprintf(stderr, "could not open table file at %s or %s\n", TABLE_FILE, TABLE_FILE_2);
|
||||
return 128;
|
||||
}
|
||||
|
||||
int bits[7];
|
||||
for (int i = 0; i < 7; ++i)
|
||||
bits[i] = 0;
|
||||
|
||||
// loop keys, check balances
|
||||
for (int i = 0, j = 0; i < argc; i+=2, ++j) {
|
||||
// convert the argv from hex to binary
|
||||
uint8_t key[32];
|
||||
key_from_hex((uint8_t*)argv[i], key);
|
||||
|
||||
uint32_t bytecount = 0;
|
||||
sscanf(argv[i+1], "%d", &bytecount);
|
||||
|
||||
int error = 0;
|
||||
uint64_t balance = 0;
|
||||
size_t recordno = 0;
|
||||
if (binary_file_search(f, key, 0, &balance, &recordno, &error)) {
|
||||
if (j < 7) bits[j] = balance > bytecount;
|
||||
if (print_balances) {
|
||||
printf("\t\"");
|
||||
print_hex(key, KEY_SIZE);
|
||||
printf("\": %lu%s", balance, (i == argc-1 ? "\n": ",\n"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (print_balances)
|
||||
printf("}");
|
||||
fclose(f);
|
||||
|
||||
if (DEBUG)
|
||||
for (int i = 0; i < 7; ++i)
|
||||
printf("bit %d: %d\n", i, bits[i]);
|
||||
|
||||
return bits[0] * 64 + bits[1] * 32 + bits[2] * 16 + bits[3] * 8 + bits[4] * 4 + bits[5] * 2 + bits[6];
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
|
||||
|
||||
// input checks
|
||||
|
||||
int mode = 0; // mode 0 is passthrough [ writes ]
|
||||
|
||||
if (argc >= 2 && strcmp(argv[1], "--credit") == 0)
|
||||
mode = 1; // mode 1 credit mode [ writes ]
|
||||
|
||||
if (argc >= 2 && strcmp(argv[1], "--check") == 0)
|
||||
mode = 2; // mode 2 check mode [ read only ]
|
||||
|
||||
if (argc >= 2 && strcmp(argv[1], "--balance") == 0)
|
||||
mode = 3; // mode 3 balance mode [ read only ]
|
||||
|
||||
|
||||
if (mode == 0) {
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "appbill requires an executable to pass execution to as an argument when running in pass through mode\n");
|
||||
return 128;
|
||||
}
|
||||
return pass_through_mode(argc, argv);
|
||||
}
|
||||
|
||||
if (argc < 3) {
|
||||
fprintf(stderr, "appbill was not supplied sufficient arguments\n");
|
||||
return 128;
|
||||
}
|
||||
|
||||
argc-=2;
|
||||
argv+=2;
|
||||
|
||||
if (mode == 1)
|
||||
return credit_mode(argc, argv);
|
||||
|
||||
if (mode == 2 || mode == 3)
|
||||
return check_mode(argc, argv, mode == 3);
|
||||
|
||||
fprintf(stderr, "unknown mode, execution should not reach here\n");
|
||||
|
||||
return 128;
|
||||
}
|
||||
19
src/conf.cpp
19
src/conf.cpp
@@ -218,11 +218,21 @@ int load_config()
|
||||
|
||||
cfg.binary = d["binary"].GetString();
|
||||
cfg.binargs = d["binargs"].GetString();
|
||||
cfg.appbill = d["appbill"].GetString();
|
||||
cfg.appbillargs = d["appbillargs"].GetString();
|
||||
|
||||
|
||||
// Populate runtime contract execution args.
|
||||
if (!cfg.binargs.empty())
|
||||
boost::split(cfg.runtime_binexec_args, cfg.binargs, boost::is_any_of(" "));
|
||||
cfg.runtime_binexec_args.insert(cfg.runtime_binexec_args.begin(), cfg.binary);
|
||||
cfg.runtime_binexec_args.insert(cfg.runtime_binexec_args.begin(), ( cfg.binary[0] == '/' ? cfg.binary : util::realpath( ctx.contractdir + "/bin/" + cfg.binary ) ) );
|
||||
|
||||
|
||||
// Populate runtime app bill args.
|
||||
if (!cfg.appbillargs.empty())
|
||||
boost::split(cfg.runtime_appbill_args, cfg.appbillargs, boost::is_any_of(" "));
|
||||
|
||||
cfg.runtime_appbill_args.insert(cfg.runtime_appbill_args.begin(), ( cfg.appbill[0] == '/' ? cfg.appbill : util::realpath( ctx.contractdir + "/bin/" + cfg.appbill ) ) );
|
||||
|
||||
// Uncomment for docker-based execution.
|
||||
// std::string volumearg;
|
||||
@@ -311,6 +321,9 @@ int save_config()
|
||||
d.AddMember("seckeyhex", rapidjson::StringRef(cfg.seckeyhex.data()), allocator);
|
||||
d.AddMember("binary", rapidjson::StringRef(cfg.binary.data()), allocator);
|
||||
d.AddMember("binargs", rapidjson::StringRef(cfg.binargs.data()), allocator);
|
||||
d.AddMember("appbill", rapidjson::StringRef(cfg.appbill.data()), allocator);
|
||||
d.AddMember("appbillargs", rapidjson::StringRef(cfg.appbillargs.data()), allocator);
|
||||
d.AddMember("listenip", rapidjson::StringRef(cfg.listenip.data()), allocator);
|
||||
d.AddMember("listenip", rapidjson::StringRef(cfg.listenip.data()), allocator);
|
||||
|
||||
rapidjson::Value peers(rapidjson::kArrayType);
|
||||
@@ -540,7 +553,7 @@ int is_schema_valid(const rapidjson::Document &d)
|
||||
const char *cfg_schema =
|
||||
"{"
|
||||
"\"type\": \"object\","
|
||||
"\"required\": [ \"mode\", \"version\", \"pubkeyhex\", \"seckeyhex\", \"binary\", \"binargs\", \"listenip\""
|
||||
"\"required\": [ \"mode\", \"version\", \"pubkeyhex\", \"seckeyhex\", \"binary\", \"binargs\", \"appbill\", \"appbillargs\", \"listenip\""
|
||||
", \"peers\", \"unl\", \"pubport\", \"peerport\", \"roundtime\""
|
||||
", \"pubmaxsize\", \"pubmaxcpm\", \"pubmaxbadmpm\", \"pubmaxcons\""
|
||||
", \"peermaxsize\", \"peermaxcpm\", \"peermaxdupmpm\", \"peermaxbadmpm\", \"peermaxbadsigpm\", \"peermaxcons\""
|
||||
@@ -552,6 +565,8 @@ int is_schema_valid(const rapidjson::Document &d)
|
||||
"\"seckeyhex\": { \"type\": \"string\" },"
|
||||
"\"binary\": { \"type\": \"string\" },"
|
||||
"\"binargs\": { \"type\": \"string\" },"
|
||||
"\"appbill\": { \"type\": \"string\" },"
|
||||
"\"appbillargs\": { \"type\": \"string\" },"
|
||||
"\"listenip\": { \"type\": \"string\" },"
|
||||
"\"peers\": {"
|
||||
"\"type\": \"array\","
|
||||
|
||||
@@ -46,6 +46,8 @@ struct contract_config
|
||||
std::string pubkey; // Contract public key bytes
|
||||
std::string seckey; // Contract secret key bytes
|
||||
std::vector<std::string> runtime_binexec_args; // Contract binary execution args used during runtime.
|
||||
std::vector<std::string> runtime_appbill_args; // Appbill execution args used during runtime.
|
||||
|
||||
|
||||
// Config elements which are loaded from the config file.
|
||||
|
||||
@@ -55,6 +57,8 @@ struct contract_config
|
||||
std::string keytype; // Key generation algorithm used by libsodium
|
||||
std::string binary; // Full path to the contract binary
|
||||
std::string binargs; // CLI arguments to pass to the contract binary
|
||||
std::string appbill; // binary to execute for appbill
|
||||
std::string appbillargs; // any arguments to supply to appbill binary by default
|
||||
std::string listenip; // The IPs to listen on for incoming connections
|
||||
std::unordered_map<std::string, ip_port_pair> peers; // Map of peers keyed by "<ip address>:<port>" concatenated format
|
||||
std::unordered_set<std::string> unl; // Unique node list (list of binary public keys)
|
||||
@@ -114,4 +118,4 @@ void change_operating_mode(const OPERATING_MODE mode);
|
||||
|
||||
} // namespace conf
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "ledger_handler.hpp"
|
||||
#include "state_handler.hpp"
|
||||
#include "cons.hpp"
|
||||
#include "../statefs/state_common.hpp"
|
||||
#include "../statefs/state_store.hpp"
|
||||
|
||||
namespace p2pmsg = fbschema::p2pmsg;
|
||||
@@ -320,9 +321,12 @@ void verify_and_populate_candidate_user_inputs()
|
||||
{
|
||||
for (const auto &[pubkey, umsgs] : p.user_messages)
|
||||
{
|
||||
// Populate user list.
|
||||
// Populate user list with this user's pubkey.
|
||||
ctx.candidate_users.emplace(pubkey);
|
||||
|
||||
// Collect valid inputs for this user.
|
||||
std::unordered_map<std::string, candidate_user_input> valid_inputs;
|
||||
|
||||
for (const usr::user_submitted_message &umsg : umsgs)
|
||||
{
|
||||
const std::string sig_hash = crypto::get_hash(umsg.sig);
|
||||
@@ -333,8 +337,6 @@ void verify_and_populate_candidate_user_inputs()
|
||||
// Verify the signature of the message content.
|
||||
if (crypto::verify(umsg.content, umsg.sig, pubkey) == 0)
|
||||
{
|
||||
// TODO: Also verify XRP payment token/AppBill requirements.
|
||||
|
||||
std::string nonce;
|
||||
std::string input;
|
||||
uint64_t maxledgerseqno;
|
||||
@@ -348,7 +350,7 @@ void verify_and_populate_candidate_user_inputs()
|
||||
// Append the hash of the message signature to get the final hash.
|
||||
hash.append(sig_hash);
|
||||
|
||||
ctx.candidate_user_inputs.try_emplace(
|
||||
valid_inputs.try_emplace(
|
||||
hash,
|
||||
candidate_user_input(pubkey, std::move(input), maxledgerseqno));
|
||||
}
|
||||
@@ -359,11 +361,81 @@ void verify_and_populate_candidate_user_inputs()
|
||||
LOG_DBG << "Duplicate user message.";
|
||||
}
|
||||
}
|
||||
|
||||
// Verify the accumulated input length with app bill before adding to candidate inputs.
|
||||
size_t total_input_len = 0;
|
||||
for (const auto &[hash, cand_input] : valid_inputs)
|
||||
total_input_len += cand_input.input.size();
|
||||
|
||||
if (verify_appbill_check(pubkey, total_input_len))
|
||||
ctx.candidate_user_inputs.merge(valid_inputs);
|
||||
|
||||
// TODO: report back to the user if the inputs didn't make it into consensus due to some reason.
|
||||
}
|
||||
}
|
||||
p2p::ctx.collected_msgs.nonunl_proposals.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the appbill and verifies whether the user has enough account balance to process the provided input.
|
||||
* @param pubkey User binary pubkey.
|
||||
* @param input_len Total bytes length of user input.
|
||||
* @return Whether the user is allowed to process the input or not.
|
||||
*/
|
||||
bool verify_appbill_check(std::string_view pubkey, const size_t input_len)
|
||||
{
|
||||
// If appbill not enabled always green light the input.
|
||||
if (conf::cfg.appbill.empty())
|
||||
return true;
|
||||
|
||||
// execute appbill in --check mode to verify this user can submit a packet/connection to the network
|
||||
// todo: this can be made more efficient, appbill --check can process 7 at a time
|
||||
|
||||
// Fill appbill args
|
||||
const int len = conf::cfg.runtime_appbill_args.size() + 4;
|
||||
char *execv_args[len];
|
||||
for (int i = 0; i < conf::cfg.runtime_appbill_args.size(); i++)
|
||||
execv_args[i] = conf::cfg.runtime_appbill_args[i].data();
|
||||
char option[] = "--check";
|
||||
execv_args[len - 4] = option;
|
||||
// add the hex encoded public key as the last parameter
|
||||
std::string hexpubkey;
|
||||
util::bin2hex(hexpubkey, reinterpret_cast<const unsigned char *>(pubkey.data()), pubkey.size());
|
||||
std::string inputsize = std::to_string(input_len);
|
||||
execv_args[len - 3] = hexpubkey.data();
|
||||
execv_args[len - 2] = inputsize.data();
|
||||
execv_args[len - 1] = NULL;
|
||||
|
||||
int pid = fork();
|
||||
if (pid == 0)
|
||||
{
|
||||
// before execution chdir into a valid the latest state data directory that contains an appbill.table
|
||||
chdir(statefs::current_ctx.datadir.c_str());
|
||||
int ret = execv(execv_args[0], execv_args);
|
||||
LOG_ERR << "Appbill process execv failed: " << ret;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// app bill in check mode takes a very short period of time to execute, typically 1ms
|
||||
// so we will blocking wait for it here
|
||||
int status = 0;
|
||||
waitpid(pid, &status, 0); //todo: check error conditions here
|
||||
status = WEXITSTATUS(status);
|
||||
if (status != 128 && status != 0)
|
||||
{
|
||||
// this user's key passed appbill
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// user's key did not pass, do not add to user input candidates
|
||||
LOG_DBG << "Appbill validation failed " << hexpubkey << " return code was " << status;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p2p::proposal create_stage0_proposal()
|
||||
{
|
||||
// The proposal we are going to emit in stage 0.
|
||||
|
||||
@@ -118,6 +118,8 @@ void broadcast_nonunl_proposal();
|
||||
|
||||
void verify_and_populate_candidate_user_inputs();
|
||||
|
||||
bool verify_appbill_check(std::string_view pubkey, const size_t input_len);
|
||||
|
||||
p2p::proposal create_stage0_proposal();
|
||||
|
||||
p2p::proposal create_stage123_proposal(vote_counter &votes);
|
||||
|
||||
@@ -101,11 +101,21 @@ int exec_contract(const contract_exec_args &args)
|
||||
|
||||
LOG_DBG << "Starting contract process...";
|
||||
|
||||
const bool using_appbill = !conf::cfg.appbill.empty();
|
||||
int len = conf::cfg.runtime_binexec_args.size() + 1;
|
||||
if (using_appbill)
|
||||
len += conf::cfg.runtime_appbill_args.size();
|
||||
|
||||
// Fill process args.
|
||||
char *execv_args[conf::cfg.runtime_binexec_args.size() + 1];
|
||||
for (int i = 0; i < conf::cfg.runtime_binexec_args.size(); i++)
|
||||
execv_args[i] = conf::cfg.runtime_binexec_args[i].data();
|
||||
execv_args[conf::cfg.runtime_binexec_args.size()] = NULL;
|
||||
char *execv_args[len];
|
||||
int j = 0;
|
||||
if (using_appbill)
|
||||
for (int i = 0; i < conf::cfg.runtime_appbill_args.size(); i++, j++)
|
||||
execv_args[i] = conf::cfg.runtime_appbill_args[i].data();
|
||||
|
||||
for (int i = 0; i < conf::cfg.runtime_binexec_args.size(); i++, j++)
|
||||
execv_args[j] = conf::cfg.runtime_binexec_args[i].data();
|
||||
execv_args[len - 1] = NULL;
|
||||
|
||||
int ret = execv(execv_args[0], execv_args);
|
||||
LOG_ERR << "Contract process execv failed: " << ret;
|
||||
@@ -718,4 +728,4 @@ void deinit()
|
||||
kill(statemon_pid, SIGINT);
|
||||
}
|
||||
|
||||
} // namespace proc
|
||||
} // namespace proc
|
||||
|
||||
@@ -197,4 +197,4 @@ std::string realpath(std::string path)
|
||||
return buffer.data();
|
||||
}
|
||||
|
||||
} // namespace util
|
||||
} // namespace util
|
||||
|
||||
@@ -80,4 +80,4 @@ std::string realpath(std::string path);
|
||||
|
||||
} // namespace util
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -7,10 +7,11 @@ mode=$1
|
||||
|
||||
hpcore=$(realpath ../..)
|
||||
|
||||
if [ "$mode" = "new" ] || [ "$mode" = "run" ] || [ "$mode" = "update" ] || [ "$mode" = "kill" ]; then
|
||||
if [ "$mode" = "new" ] || [ "$mode" = "update" ] || [ "$mode" = "run" ] || [ "$mode" = "check" ] || \
|
||||
[ "$mode" = "connect" ] || [ "$mode" = "kill" ] || [ "$mode" = "reboot" ] || [ "$mode" = "ssh" ]; then
|
||||
echo ""
|
||||
else
|
||||
echo "Invalid command. new | run | update | kill expected."
|
||||
echo "Invalid command. [ new | update | run <N> | check <N> | connect <N> | kill <N> | reboot <N> | ssh <N> <custom command> ] expected."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -22,11 +23,39 @@ if [ $mode = "run" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ $mode = "check" ]; then
|
||||
let nodeid=$2-1
|
||||
vmip=${vmips[$nodeid]}
|
||||
sshpass -p $vmpass ssh geveo@$vmip 'echo hpcore pid:$(pidof hpcore) hpstatemon pid:$(pidof hpstatemon)'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ $mode = "connect" ]; then
|
||||
let nodeid=$2-1
|
||||
vmip=${vmips[$nodeid]}
|
||||
sshpass -p $vmpass ssh geveo@$vmip 'tail -f nohup.out'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ $mode = "kill" ]; then
|
||||
let nodeid=$2-1
|
||||
vmip=${vmips[$nodeid]}
|
||||
sshpass -p $vmpass ssh geveo@$vmip 'sudo kill $(pidof hpcore)'
|
||||
sshpass -p $vmpass ssh geveo@$vmip 'sudo kill $(pidof hpstatemon)'
|
||||
sshpass -p $vmpass ssh geveo@$vmip 'sudo kill $(pidof hpcore) > /dev/null 2>&1'
|
||||
sshpass -p $vmpass ssh geveo@$vmip 'sudo kill $(pidof hpstatemon) > /dev/null 2>&1'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ $mode = "reboot" ]; then
|
||||
let nodeid=$2-1
|
||||
vmip=${vmips[$nodeid]}
|
||||
sshpass -p $vmpass ssh geveo@$vmip 'sudo reboot'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ $mode = "ssh" ]; then
|
||||
let nodeid=$2-1
|
||||
vmip=${vmips[$nodeid]}
|
||||
sshpass -p $vmpass ssh geveo@$vmip $3
|
||||
exit 0
|
||||
fi
|
||||
|
||||
|
||||
Reference in New Issue
Block a user