Newer
Older
/*
* Authors:
* Alexander Lysikov <alexander.lysikov@demlabs.net>
* DeM Labs Inc. https://demlabs.net
* Kelvin Project https://github.com/kelvinblockchain
* Copyright (c) 2019
* All rights reserved.
This file is part of DAP (Deus Applications Prototypes) the open source project
DAP (Deus Applicaions Prototypes) is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
DAP is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with any DAP based project. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <string.h>
#include <errno.h>
#ifndef _WIN32
#include <poll.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <fcntl.h>
//#include <sys/poll.h>
//#include <sys/select.h>
#include <netinet/in.h>
#include <sys/un.h>
//#define closesocket close
//typedef int SOCKET;
//#define SOCKET_ERROR -1 // for win32 = (-1)
//#define INVALID_SOCKET -1 // for win32 = (SOCKET)(~0)
// for Windows
#else
#include <winsock2.h>
#include <windows.h>
#include <mswsock.h>
#include <ws2tcpip.h>
#include <io.h>
#include "iputils/iputils.h"
#include "dap_common.h"
#include "dap_strfuncs.h"
#include "dap_list.h"
#include "dap_chain_node_cli_cmd.h"
#include "dap_chain_node_cli.h"
//#include "dap_chain_node_cli.h"
#define LOG_TAG "chain_node_cli"
#define MAX_CONSOLE_CLIENTS 16
static SOCKET server_sockfd = -1; // network or local unix
uint32_t l_listen_port = 0;
static dap_chain_node_cmd_item_t * s_commands = NULL;
/**
* Wait for data
* timeout - timeout in ms
* [Specifying a negative value in timeout means an infinite timeout.]
* [Specifying a timeout of zero causes poll() to return immediately, even if no file descriptors are ready.]
* return zero if the time limit expired
* return: >0 if data is present to read
* return: -1 if error
*/
{
struct pollfd fds;
int res;
fds.fd = socket;
// POLLIN - received data
// POLLNVAL - closed the socket on our side
// POLLHUP - closed the socket on another side (does not work! Received POLLIN and the next reading returns 0 bytes)
fds.events = POLLIN; // | | POLLNVAL | POLLHUP | POLLERR | POLLPRI
res = poll(&fds, 1, timeout);
// since POLLIN=(POLLRDNORM | POLLRDBAND), then maybe revents=POLLRDNORM
if(res == 1 && !(fds.revents & POLLIN)) //if(res==1 && fds.revents!=POLLIN)
return -1;
return res;
}
/**
* Check socket for validity
*/
static bool is_valid_socket(SOCKET sock)
{
struct pollfd fds;
fds.fd = sock;
fds.events = POLLIN;
int count_desc = poll(&fds, 1, 0);
if(count_desc == -1)
return false;
if(count_desc > 0)
{
// feature of disconnection under Windows
// under Windows, with socket closed fds.revents=POLLHUP, in Unix fds.events = POLLIN
if(fds.revents & (POLLERR | POLLHUP | POLLNVAL))
return false;
// feature of disconnection under Unix (QNX)
// under Windows, with socket closed res = 0, in Unix res = -1
char buf[2];
long res = recv(sock, buf, 1, MSG_PEEK); // MSG_PEEK The data is treated as unread and the next recv() function shall still return this data.
if(res < 0)
return false;
// data in the buffer must be(count_desc>0), but read 0 bytes(res=0)
if(!res && (fds.revents & POLLIN))
return false;
}
return true;
}
/**
* Read from socket
*
* timeout in milliseconds
* return the number of read bytes (-1 err or -2 timeout)
*/
long s_recv(SOCKET sock, unsigned char *buf, size_t bufsize, int timeout)
{
struct pollfd fds;
fds.fd = sock;
fds.events = POLLIN; // | POLLNVAL | POLLHUP | POLLERR | POLLPRI;// | POLLRDNORM;//POLLOUT |
res = poll(&fds, 1, timeout);
if(res == 1 && !(fds.revents & POLLIN))
return -1;
if(!res) // timeout
return -2;
if(res < 1) {
return -1;
}
// res = read(sock, (char*) buf, bufsize);
res = recv(sock, (char*) buf, bufsize, 0); //MSG_WAITALL
if(res <= 0) { //EINTR=4 ENOENT=2 EINVAL=22 ECONNRESET=254
printf("[s_recv] recv()=%ld errno=%d\n", res, errno);
}
return res;
}
/**
* Reading from the socket till arrival the specified string
*
* stop_str - string to which reading will continue
* del_stop_str - удалять ли строку для поиска в конце
* timeout - in ms
* return: string (if waited for final characters) or NULL, if the string requires deletion
*/
char* s_get_next_str( SOCKET nSocket, int *dwLen, const char *stop_str, bool del_stop_str, int timeout )
{
bool bSuccess = false;
long nRecv = 0; // count of bytes received
size_t stop_str_len = (stop_str) ? strlen(stop_str) : 0;
if(!stop_str_len)
return NULL;
size_t lpszBuffer_len = 256;
char *lpszBuffer = DAP_NEW_Z_SIZE(char, lpszBuffer_len);
// received string will not be larger than MAX_REPLY_LEN
while(1) //nRecv < MAX_REPLY_LEN)
{
long ret = s_recv(nSocket, (unsigned char *) (lpszBuffer + nRecv), 1, timeout);
//int ret = recv(nSocket,lpszBuffer+nRecv,1, 0);
if(ret <= 0)
{
break;
}
nRecv += ret;
//printf("**debug** socket=%d read %d bytes '%0s'",nSocket, ret, (lpszBuffer + nRecv));
{
lpszBuffer_len *= 2;
lpszBuffer = (char*) realloc(lpszBuffer, lpszBuffer_len);
}
// found the required string
if(!strncasecmp(lpszBuffer + nRecv - stop_str_len, stop_str, stop_str_len)) {
bSuccess = true;
break;
}
}
};
if(bSuccess) {
if(del_stop_str) {
if(dwLen)
}
else {
lpszBuffer[nRecv] = '\0';
if(dwLen)
}
lpszBuffer = DAP_REALLOC(lpszBuffer,(size_t) *dwLen + 1);
return lpszBuffer;
}
// in case of an error or missing string
if(dwLen)
*dwLen = 0;
free(lpszBuffer);
return NULL;
}
/**
* threading function for processing a request from a client
*/
static void* thread_one_client_func(void *args)
{
SOCKET newsockfd = (SOCKET) (intptr_t) args;
log_it(L_INFO, "new connection sockfd=%d", newsockfd);
int str_len, marker = 0;
int timeout = 5000; // 5 sec
int argc = 0;
dap_list_t *cmd_param_list = NULL;
while(1)
{
int is_data = s_poll(newsockfd, timeout);
if(!is_data)
continue;
if(is_data < 0)
break;
int is_valid = is_valid_socket(newsockfd);
if(!is_valid)
{
break;
}
char *str_header = s_get_next_str(newsockfd, &str_len, "\r\n", true, timeout);
if(!str_header)
break;
if(str_header && strlen(str_header) == 0) {
marker++;
if(marker == 1)
continue;
}
if(marker == 1) {
cmd_param_list = dap_list_append(cmd_param_list, str_header);
//printf("g_list_append argc=%d command=%s ", argc, str_header);
argc++;
}
else
free(str_header);
if(marker == 2) {
dap_list_t *list = cmd_param_list;
// form command
unsigned int argc = dap_list_length(list);
// command is found
char *cmd_name = list->data;
// execute command
char *str_cmd = dap_strdup_printf("%s", cmd_name);
dap_chain_node_cmd_item_t *l_cmd = dap_chain_node_cli_cmd_find(cmd_name);
int res = -1;
char *str_reply = NULL;
str_cmd = dap_strdup_printf("%s;%s", str_cmd, list->data);
list = dap_list_next(list);
}
log_it(L_INFO, "execute command=%s", str_cmd);
// exec command
if(l_cmd && l_argv && l_cmd->func)
res = (*(l_cmd->func))(argc, l_argv, &str_reply);
else if (l_cmd){
log_it(L_WARNING,"NULL arguments for input for command \"%s\"", str_cmd);
}else {
log_it(L_WARNING,"No function for command \"%s\" but it registred?!", str_cmd);
}
l_verbose = dap_chain_node_cli_find_option_val(l_argv, 1, argc, "-verbose", NULL);
dap_strfreev(l_argv);
str_reply = dap_strdup_printf("can't recognize command=%s", str_cmd);
log_it(L_ERROR, str_reply);
char *reply_body;
if(l_verbose)
reply_body = dap_strdup_printf("%d\r\nret_code: %d\r\n%s\r\n", res, res, (str_reply) ? str_reply : "");
reply_body = dap_strdup_printf("%d\r\n%s\r\n", res, (str_reply) ? str_reply : "");
// return the result of the command function
char *reply_str = dap_strdup_printf("HTTP/1.1 200 OK\r\n"
"Content-Length: %d\r\n\r\n"
"%s",
strlen(reply_body), reply_body);
/*int ret = */ send(newsockfd, reply_str, strlen(reply_str) ,0);
DAP_DELETE(str_reply);
DAP_DELETE(reply_str);
DAP_DELETE(reply_body);
dap_list_free_full(cmd_param_list, free);
break;
}
}
int cs = closesocket(newsockfd);
log_it(L_INFO, "close connection=%d sockfd=%d", cs, newsockfd);
return NULL;
}
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
#ifdef _WIN32
char *p_get_next_str( HANDLE hPipe, int *dwLen, const char *stop_str, bool del_stop_str, int timeout )
{
bool bSuccess = false;
long nRecv = 0; // count of bytes received
size_t stop_str_len = (stop_str) ? strlen(stop_str) : 0;
// if there is nothing to look for
if(!stop_str_len)
return NULL;
size_t lpszBuffer_len = 256;
char *lpszBuffer = DAP_NEW_Z_SIZE(char, lpszBuffer_len);
// received string will not be larger than MAX_REPLY_LEN
while( 1 ) //nRecv < MAX_REPLY_LEN)
{
long ret = 0;
// read one byte
// long ret = s_recv( nSocket, (unsigned char *) (lpszBuffer + nRecv), 1, timeout);
bSuccess = ReadFile( hPipe, lpszBuffer + nRecv,
lpszBuffer_len - nRecv, (LPDWORD)&ret, NULL );
//int ret = recv(nSocket,lpszBuffer+nRecv,1, 0);
if ( ret <= 0 || !bSuccess )
break;
nRecv += ret;
//printf("**debug** socket=%d read %d bytes '%0s'",nSocket, ret, (lpszBuffer + nRecv));
while((nRecv + 1) >= (long) lpszBuffer_len)
{
lpszBuffer_len *= 2;
lpszBuffer = (char*) realloc(lpszBuffer, lpszBuffer_len);
}
// search for the required string
if(nRecv >= (long) stop_str_len) {
// found the required string
if(!strncasecmp(lpszBuffer + nRecv - stop_str_len, stop_str, stop_str_len)) {
bSuccess = true;
break;
}
}
};
// end reading
if(bSuccess) {
// delete the searched string
if(del_stop_str) {
lpszBuffer[nRecv - (long) stop_str_len] = '\0';
if(dwLen)
*dwLen =(int) nRecv - (int) stop_str_len;
}
else {
lpszBuffer[nRecv] = '\0';
if(dwLen)
*dwLen = (int) nRecv;
}
lpszBuffer = DAP_REALLOC(lpszBuffer,(size_t) *dwLen + 1);
return lpszBuffer;
}
// in case of an error or missing string
if(dwLen)
*dwLen = 0;
free(lpszBuffer);
return NULL;
}
/**
* threading function for processing a request from a client
*/
static void *thread_pipe_client_func( void *args )
{
HANDLE hPipe = (HANDLE)args;
// SOCKET newsockfd = (SOCKET) (intptr_t) args;
log_it(L_INFO, "new connection pipe = %X", hPipe );
int str_len, marker = 0;
int timeout = 5000; // 5 sec
int argc = 0;
dap_list_t *cmd_param_list = NULL;
while( 1 )
{
// wait data from client
// int is_data = s_poll( newsockfd, timeout );
// timeout
// if(!is_data)
// continue;
// error (may be socket closed)
// if(is_data < 0)
// break;
// int is_valid = is_valid_socket(newsockfd);
// if(!is_valid)
// {
// break;
// }
// receiving http header
char *str_header = p_get_next_str( hPipe, &str_len, "\r\n", true, timeout );
// bad format
if(!str_header)
break;
if ( str_header && strlen(str_header) == 0) {
marker++;
if(marker == 1)
continue;
}
// filling parameters of command
if ( marker == 1 ) {
cmd_param_list = dap_list_append( cmd_param_list, str_header );
//printf("g_list_append argc=%d command=%s ", argc, str_header);
argc ++;
}
else
free( str_header );
if ( marker == 2 ) {
dap_list_t *list = cmd_param_list;
// form command
unsigned int argc = dap_list_length( list );
// command is found
if ( argc >= 1) {
int l_verbose = 0;
char *cmd_name = list->data;
list = dap_list_next( list );
// execute command
char *str_cmd = dap_strdup_printf( "%s", cmd_name );
dap_chain_node_cmd_item_t *l_cmd = dap_chain_node_cli_cmd_find( cmd_name );
int res = -1;
char *str_reply = NULL;
if ( l_cmd ) {
while( list ) {
str_cmd = dap_strdup_printf( "%s;%s", str_cmd, list->data );
list = dap_list_next(list);
}
log_it(L_INFO, "execute command = %s", str_cmd );
// exec command
char **l_argv = dap_strsplit( str_cmd, ";", -1 );
// Call the command function
if ( l_cmd && l_argv && l_cmd->func )
res = (* (l_cmd->func))( argc, l_argv, &str_reply );
else if ( l_cmd ) {
log_it(L_WARNING,"NULL arguments for input for command \"%s\"", str_cmd );
}else {
log_it(L_WARNING,"No function for command \"%s\" but it registred?!", str_cmd );
}
// find '-verbose' command
l_verbose = dap_chain_node_cli_find_option_val( l_argv, 1, argc, "-verbose", NULL );
dap_strfreev( l_argv );
} else {
str_reply = dap_strdup_printf("can't recognize command = %s", str_cmd );
log_it( L_ERROR, str_reply );
}
char *reply_body;
if(l_verbose)
reply_body = dap_strdup_printf("%d\r\nret_code: %d\r\n%s\r\n", res, res, (str_reply) ? str_reply : "");
else
reply_body = dap_strdup_printf("%d\r\n%s\r\n", res, (str_reply) ? str_reply : "");
// return the result of the command function
char *reply_str = dap_strdup_printf( "HTTP/1.1 200 OK\r\n"
"Content-Length: %d\r\n\r\n"
"%s",
strlen(reply_body), reply_body );
int ret;// = send( newsockfd, reply_str, strlen(reply_str) ,0 );
WriteFile( hPipe, reply_str, strlen(reply_str), (LPDWORD)&ret, NULL );
DAP_DELETE(str_reply);
DAP_DELETE(reply_str);
DAP_DELETE(reply_body);
DAP_DELETE(str_cmd);
}
dap_list_free_full(cmd_param_list, free);
break;
}
}
// close connection
// int cs = closesocket(newsockfd);
log_it( L_INFO, "close connection pipe = %X", hPipe );
FlushFileBuffers( hPipe );
DisconnectNamedPipe( hPipe );
CloseHandle( hPipe );
return NULL;
}
/**
* main threading server function pipe win32
*/
static void* thread_pipe_func( void *args )
{
HANDLE hPipe = INVALID_HANDLE_VALUE, hThread = NULL;
static const char *cPipeName = "\\\\.\\pipe\\node_cli.pipe";
/// printf( "\nPipe Server: Main thread awaiting client connection on %s\n", lpszPipename );
hPipe = CreateNamedPipe(
cPipeName, // pipe name
PIPE_ACCESS_DUPLEX, // read/write access
PIPE_TYPE_MESSAGE | // message type pipe
PIPE_READMODE_MESSAGE | // message-read mode
PIPE_WAIT, // blocking mode
PIPE_UNLIMITED_INSTANCES, // max. instances
4096, // output buffer size
4096, // input buffer size
0, // client time-out
NULL ); // default security attribute
if ( hPipe == INVALID_HANDLE_VALUE ) {
log_it( L_ERROR, "CreateNamedPipe failed, GLE = %d.\n", GetLastError() );
return NULL;
}
fConnected = ConnectNamedPipe( hPipe, NULL ) ? TRUE : ( GetLastError() == ERROR_PIPE_CONNECTED );
{
log_it( L_INFO, "Client %X connected, creating a processing thread.\n", hPipe );
pthread_create( &threadId, NULL, thread_pipe_client_func, hPipe );
pthread_detach( threadId );
/**
* main threading server function
*/
static void* thread_main_func(void *args)
{
SOCKET sockfd = (SOCKET) (intptr_t) args;
SOCKET newsockfd;
log_it( L_INFO, "Server start socket = %s", dap_config_get_item_str( g_config, "conserver", "listen_unix_socket_path") );
while(1)
{
pthread_t threadId;
struct sockaddr_in peer;
socklen_t size = sizeof(peer);
if((newsockfd = accept(sockfd, (struct sockaddr*) &peer, &size)) == (SOCKET) -1) {
log_it(L_ERROR, "new connection break newsockfd=%d", newsockfd);
break;
}
// create child thread for a client connection
pthread_create(&threadId, NULL, thread_one_client_func, (void*) (intptr_t) newsockfd);
// in order to thread not remain in state "dead" after completion
pthread_detach(threadId);
};
int cs = closesocket(sockfd);
log_it(L_INFO, "Exit server thread=%d socket=%d", cs, sockfd);
return NULL;
}
/**
* Write text to reply string
*/
void dap_chain_node_cli_set_reply_text(char **str_reply, const char *str, ...)
{
if(str_reply) {
if(*str_reply) {
assert(! *str_reply );
DAP_DELETE(*str_reply);
*str_reply = NULL;
}
va_list args;
va_start(args, str);
*str_reply = dap_strdup_vprintf(str, args); //*str_reply = dap_strdup(str);
va_end(args);
}
}
/**
* find option value
*
* return index of string in argv, or 0 if not found
*/
int dap_chain_node_cli_find_option_val( char** argv, int arg_start, int arg_end, const char *opt_name, const char **opt_value)
{
int arg_index = arg_start;
const char *arg_string;
while(arg_index < arg_end)
{
char * l_argv_cur = argv[arg_index];
arg_string = l_argv_cur;
if(arg_string && opt_name && arg_string[0] && opt_name[0] && !strcmp(arg_string, opt_name)) {
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
// find opt_value
if(opt_value) {
arg_string = argv[++arg_index];
if(arg_string) {
*opt_value = arg_string;
return arg_index;
}
}
else
// need only opt_name
return arg_index;
}
arg_index++;
}
return 0;
}
/**
* @brief s_cmd_item_create
* @param a_name
* @param func
* @param doc
* @param doc_ex
* @return
*/
void dap_chain_node_cli_cmd_item_create(const char * a_name, cmdfunc_t *a_func, const char *a_doc, const char *a_doc_ex)
{
dap_chain_node_cmd_item_t *l_cmd_item = DAP_NEW_Z(dap_chain_node_cmd_item_t);
l_cmd_item->doc = strdup( a_doc);
l_cmd_item->doc_ex = strdup( a_doc_ex);
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
HASH_ADD_STR(s_commands,name,l_cmd_item);
log_it(L_DEBUG,"Added command %s",l_cmd_item->name);
}
/**
* @brief dap_chain_node_cli_command_get_first
* @return
*/
dap_chain_node_cmd_item_t* dap_chain_node_cli_cmd_get_first()
{
return s_commands;
}
/**
* @brief dap_chain_node_cli_command_find
* @param a_name
* @return
*/
dap_chain_node_cmd_item_t* dap_chain_node_cli_cmd_find(const char *a_name)
{
dap_chain_node_cmd_item_t *l_cmd_item = NULL;
HASH_FIND_STR(s_commands,a_name,l_cmd_item);
return l_cmd_item;
}
/**
* Initialization of the server side of the interaction
* with the console kelvin-node-cli
*
* return 0 if OK, -1 error
*/
int dap_chain_node_cli_init(dap_config_t * g_config)
{
struct sockaddr_un l_server_addr={0};
l_server_addr.sun_family = AF_UNIX;
snprintf(l_server_addr.sun_path,sizeof(l_server_addr.sun_path), "%s", dap_config_get_item_str( g_config, "conserver", "listen_unix_socket_path") );
bool l_conserver_enabled = dap_config_get_item_bool_default( g_config, "conserver", "enabled", true );
if ( !l_conserver_enabled ) {
log_it( L_WARNING, "Console Server is dissabled." );
return 0;
}
#ifdef __WIN32
WSADATA wsaData;
int ret = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (ret != 0) {
log_it(L_CRITICAL, "Couldn't init Winsock DLL, error: %d", ret);
return 2;
}
#endif
dap_chain_node_cli_cmd_item_create("global_db", com_global_db, "Work with global database",
"global_db cells add -cell <cell id> \n"
"global_db flush \n\n"
// "global_db wallet_info set -addr <wallet address> -cell <cell id> \n\n"
);
dap_chain_node_cli_cmd_item_create("node", com_node, "Work with node",
"node add -net <net name> -addr {<node address> | -alias <node alias>} {-port <port>} -cell <cell id> {-ipv4 <ipv4 external address> | -ipv6 <ipv6 external address>}\n\n"
"node del -net <net name> -addr <node address> | -alias <node alias>\n\n"
"node link {add|del} -net <net name> {-addr <node address> | -alias <node alias>} -link <node address>\n\n"
"node alias -addr <node address> -alias <node alias>\n\n"
"node connect {<node address> | -alias <node alias> | auto}\n\n"
"node handshake {<node address> | -alias <node alias>}\n"
"node dump -net <net name> [ -addr <node address> | -alias <node alias>] [-full]\n\n"
dap_chain_node_cli_cmd_item_create ("ping", com_ping, "Send ICMP ECHO_REQUEST to network hosts",
dap_chain_node_cli_cmd_item_create ("traceroute", com_traceroute, "Print the hops and time of packets trace to network host",
dap_chain_node_cli_cmd_item_create ("tracepath", com_tracepath, "Traces path to a network host along this path",
dap_chain_node_cli_cmd_item_create ("help", com_help, "Description of command parameters",
"help [<command>]\n"
"\tObtain help for <command> or get the total list of the commands\n"
);
dap_chain_node_cli_cmd_item_create ("?", com_help, "Synonym for \"help\"",
"? [<command>]\n"
"\tObtain help for <command> or get the total list of the commands\n"
);
dap_chain_node_cli_cmd_item_create("wallet", com_tx_wallet, "Wallet operations",
"wallet [new -w <wallet_name> [-sign <sign_type>] [-restore <hex value>] [-net <net_name>] [-force]| list | info -addr <addr> -w <wallet_name> -net <net_name>]\n");
// Token commands
dap_chain_node_cli_cmd_item_create ("token_decl", com_token_decl, "Token declaration",
"token_decl -net <net name> -chain <chain name> -token <token ticker> -total_supply <total supply> -signs_total <sign total> -signs_emission <signs for emission> -certs <certs list>\n"
"\t Declare new token for <netname>:<chain name> with ticker <token ticker>, maximum emission <total supply> and <signs for emission> from <signs total> signatures on valid emission\n"
"token_decl_sign -net <net name> -chain <chain name> -datum <datum_hash> -certs <certs list>\n"
"\t Sign existent <datum hash> in mempool with <certs list>\n"
);
dap_chain_node_cli_cmd_item_create ("token_decl_sign", com_token_decl_sign, "Token declaration add sign",
"token_decl_sign -net <net name> -chain <chain name> -datum <datum_hash> -certs <certs list>\n"
"\t Sign existent <datum hash> in mempool with <certs list>\n"
);
dap_chain_node_cli_cmd_item_create ("token_emit", com_token_emit, "Token emission",
"token_emit -net <net name> -chain_emission <chain for emission> -chain_base_tx <chain for base tx> -addr <addr> -token <token ticker> -certs <cert> -emission_value <val>\n");
dap_chain_node_cli_cmd_item_create ("mempool_list", com_mempool_list, "List mempool entries for selected chain network and chain id",
"mempool_list -net <net name> -chain <chain name>\n");
dap_chain_node_cli_cmd_item_create ("mempool_proc", com_mempool_proc, "Proc mempool entries for selected chain network and chain id",
"mempool_proc -net <net name> -chain <chain name>\n");
dap_chain_node_cli_cmd_item_create ("mempool_delete", com_mempool_delete, "Delete datum with hash <datum hash>",
"mempool_delete -net <net name> -chain <chain name> -datum <datum hash>\n");
dap_chain_node_cli_cmd_item_create ("tx_create", com_tx_create, "Make transaction",
"tx_create -net <net name> -chain <chain name> -from_wallet <name> -to_addr <addr> -token <token ticker> -value <value> [-fee <addr> -value_fee <val>]\n" );
dap_chain_node_cli_cmd_item_create ("tx_cond_create", com_tx_cond_create, "Make cond transaction",
dap_chain_node_cli_cmd_item_create ("tx_verify", com_tx_verify, "Verifing transaction",
"tx_verify -wallet <wallet name> \n" );
dap_chain_node_cli_cmd_item_create("tx_history", com_tx_history, "Transaction history (for address or by hash)",
"tx_history [-addr <addr> | -w <wallet name> | -tx <tx_hash>] -net <net name> -chain <chain name>\n");
// vpn client
dap_chain_node_cli_cmd_item_create ("vpn_client", com_vpn_client, "VPN client control",
"vpn_client [start -addr <server address> -port <server port>| stop | status]\n");
// Log
dap_chain_node_cli_cmd_item_create ("print_log", com_print_log, "Print log info",
"print_log [ts_after <timestamp >] [limit <line numbers>]\n" );
// Statisticss
dap_chain_node_cli_cmd_item_create("stats", com_stats, "Print statistics",
"stats cpu");
// Exit
dap_chain_node_cli_cmd_item_create ("exit", com_exit, "Stop application and exit",
"exit\n" );
l_listen_port = dap_config_get_item_uint16_default( g_config, "conserver", "listen_port_tcp",0);
const char * l_listen_unix_socket_path = dap_config_get_item_str( g_config, "conserver", "listen_unix_socket_path");
const char * l_listen_unix_socket_permissions_str = dap_config_get_item_str( g_config, "conserver", "listen_unix_socket_permissions");
mode_t l_listen_unix_socket_permissions = 0770;
if ( l_listen_unix_socket_path && l_listen_unix_socket_permissions ) {
if ( l_listen_unix_socket_permissions_str ) {
dap_sscanf(l_listen_unix_socket_permissions_str,"%ou", &l_listen_unix_socket_permissions );
log_it( L_INFO, "Console interace on path %s (%04o) ", l_listen_unix_socket_path, l_listen_unix_socket_permissions );
#ifndef _WIN32
if ( server_sockfd >= 0 ) {
dap_chain_node_cli_delete( );
server_sockfd = 0;
}
// create socket
sockfd = socket( AF_UNIX, SOCK_STREAM, 0 );
if( sockfd == INVALID_SOCKET )
return -1;
//int gdsg = sizeof(struct sockaddr_un);
if ( access( l_listen_unix_socket_path , R_OK) != -1 )
unlink( l_listen_unix_socket_path );
if( bind(sockfd, (const struct sockaddr*) &l_server_addr, sizeof(struct sockaddr_un)) == SOCKET_ERROR) {
// errno = EACCES 13 Permission denied
if ( errno == EACCES ) // EACCES=13
log_it( L_ERROR, "Server can't start(err=%d). Can't create file=%s [Permission denied]", errno,
l_listen_unix_socket_path );
log_it( L_ERROR, "Server can't start(err=%d). May be problem with file=%s?", errno, l_listen_unix_socket_path );
chmod(l_listen_unix_socket_path,l_listen_unix_socket_permissions);
if( pthread_create(&threadId, NULL, thread_pipe_func, (void*) (intptr_t) sockfd) != 0 ) {
closesocket( sockfd );
else if (l_listen_port ){
const char *l_listen_addr_str = dap_config_get_item_str(g_config, "conserver", "listen_address");
log_it( L_INFO, "Console interace on addr %s port %u ", l_listen_addr_str, l_listen_port );
#ifdef _WIN32
struct in_addr _in_addr = { { .S_addr = htonl(INADDR_LOOPBACK) } };
server_addr.sin_addr = _in_addr;
server_addr.sin_port = l_listen_port;
#else
inet_pton( AF_INET, l_listen_addr_str, &server_addr.sin_addr );
server_addr.sin_port = htons( (uint16_t)l_listen_port );
#endif
// create socket
if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET ) {
#ifdef __WIN32
_set_errno(WSAGetLastError());
#endif
log_it( L_ERROR, "Console Server: can't create socket, err %d", errno );
}
// connecting the address with a socket
if ( bind(sockfd, (struct sockaddr *) &server_addr, sizeof(server_addr)) == SOCKET_ERROR ) {
#ifdef __WIN32
_set_errno(WSAGetLastError());
#endif
log_it( L_ERROR, "Console Server: can't bind socket, err %d", errno );
}else {
log_it (L_INFO, "Not defined console interface");
return 0;
if( pthread_create(&l_thread_id, NULL, thread_main_func, (void*) (intptr_t) sockfd) != 0 ) {
// in order to thread not remain in state "dead" after completion
pthread_detach( l_thread_id );
server_sockfd = sockfd;
return 0;
}
/**
* Deinitialization of the server side
*
*/
void dap_chain_node_cli_delete(void)
{
if(server_sockfd >= 0)
closesocket(server_sockfd);