Skip to content
Snippets Groups Projects
dap_chain_node_cli.c 47.1 KiB
Newer Older
Roman Khlopkov's avatar
Roman Khlopkov committed
 * Dmitriy A. Gerasimov <gerasimov.dmitriy@demlabs.net>
 * Alexander Lysikov <alexander.lysikov@demlabs.net>
 * DeM Labs Inc.   https://demlabs.net
 * Cellframe  https://cellframe.net
 * Copyright  (c) 2019-2021
 * All rights reserved.
 This file is part of Cellframe SDK
 Cellframe SDK 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.

 Cellframe SDK 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 Cellframe SDK based project.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <string.h>
#include <errno.h>
dmitriy.gerasimov's avatar
dmitriy.gerasimov committed
#include <assert.h>
//#include <glib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
//#include <unistd.h> // for close
#include <fcntl.h>
//#include <sys/poll.h>
//#include <sys/select.h>
#include <netinet/in.h>
#include <sys/un.h>
Roman Khlopkov's avatar
Roman Khlopkov committed
#include <sys/stat.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>
ANTA's avatar
ANTA committed
#include <windows.h>
#include <mswsock.h>
#include <ws2tcpip.h>
#include <io.h>
ANTA's avatar
ANTA committed
#include <pthread.h>

#include "iputils/iputils.h"
#include "dap_config.h"
#include "dap_strfuncs.h"
#include "dap_file_utils.h"
#include "dap_list.h"
#include "dap_chain_node_cli_cmd.h"
Dmitriy A. Gerasimov's avatar
Dmitriy A. Gerasimov committed
#include "dap_chain_node_client.h"
#include "dap_chain_node_cli_cmd_tx.h"
#include "dap_chain_node_cli.h"

//#include "dap_chain_node_cli.h"

#define LOG_TAG "chain_node_cli"

ANTA's avatar
ANTA committed
#define MAX_CONSOLE_CLIENTS 16

static SOCKET server_sockfd = -1; // network or local unix
uint32_t l_listen_port = 0;
bool s_debug_cli = false;
ANTA's avatar
ANTA committed

#ifdef _WIN32
  #define poll WSAPoll
#endif
dmitriy.gerasimov's avatar
dmitriy.gerasimov committed
static dap_chain_node_cmd_item_t * s_commands = NULL;
cellframe.doc's avatar
cellframe.doc committed
 * @brief int s_poll
 * 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
 * @param socket
 * @param timeout
 * @return int
static int s_poll( int sd, int timeout )
struct pollfd fds = {.fd = sd, .events = POLLIN};
int res;

    return  (res == 1 && !(fds.revents & POLLIN)) ? -1 : res;
cellframe.doc's avatar
cellframe.doc committed

cellframe.docs's avatar
cellframe.docs committed
 * @brief is_valid_socket
 * @param sock
 * @return true
 * @return false
static int is_valid_socket(SOCKET sd)
struct pollfd fds = {.fd = sd, .events = POLLIN};
int res;

    if ( 0 > (res = poll(&fds, 1, 0)) )
    // event with an error code
        // 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
        if ( 0 > (res = recv(sd, buf, 1, MSG_PEEK)) ) // MSG_PEEK  The data is treated as unread and the next recv() function shall still return this data.
        // data in the buffer must be(count_desc>0), but read 0 bytes(res=0)
        if(!res && (fds.revents & POLLIN))
            return false;
    }
cellframe.docs's avatar
cellframe.docs committed
 * @brief s_recv
 * timeout in milliseconds
 * return the number of read bytes (-1 err or -2 timeout)
 * @param sock
 * @param buf
 * @param bufsize
 * @param timeout
 * @return long
long s_recv(SOCKET sd, unsigned char *buf, size_t bufsize, int timeout)
struct pollfd fds = {.fd = sd, .events = POLLIN};
int res;

    if ( !(res = poll(&fds, 1, timeout)) )

    if ( (res == 1) && !(fds.revents & POLLIN))
    if ( 0 >= (res = recv(sd, (char *)buf, bufsize, 0)) )
        printf("[s_recv] recv()->%d, errno: %d\n", res, errno);

cellframe.doc's avatar
cellframe.doc committed

cellframe.docs's avatar
cellframe.docs committed
 * @brief s_get_next_str
 * 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
 * @param nSocket
 * @param dwLen
 * @param stop_str
 * @param del_stop_str
 * @param timeout
 * @return char*
ANTA's avatar
ANTA committed
char* s_get_next_str( SOCKET nSocket, int *dwLen, const char *stop_str, bool del_stop_str, int timeout )
dmitriy.gerasimov's avatar
dmitriy.gerasimov committed
    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
dmitriy.gerasimov's avatar
dmitriy.gerasimov committed
    size_t lpszBuffer_len = 256;
    char *lpszBuffer = DAP_NEW_Z_SIZE(char, lpszBuffer_len);
    // received string will not be larger than MAX_REPLY_LEN
ANTA's avatar
ANTA committed

        // read one byte
dmitriy.gerasimov's avatar
dmitriy.gerasimov committed
        long ret = s_recv(nSocket, (unsigned char *) (lpszBuffer + nRecv), 1, timeout);
        //int ret = recv(nSocket,lpszBuffer+nRecv,1, 0);
        //printf("**debug** socket=%d read  %d bytes '%0s'",nSocket, ret, (lpszBuffer + nRecv));
dmitriy.gerasimov's avatar
dmitriy.gerasimov committed
        while((nRecv + 1) >= (long) lpszBuffer_len)
        {
            lpszBuffer_len *= 2;
            lpszBuffer = (char*) realloc(lpszBuffer, lpszBuffer_len);
        }
        // search for the required string
dmitriy.gerasimov's avatar
dmitriy.gerasimov committed
        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;
            }
        }
    };
ANTA's avatar
ANTA committed

    // end reading
ANTA's avatar
ANTA committed

        // delete the searched string
dmitriy.gerasimov's avatar
dmitriy.gerasimov committed
            lpszBuffer[nRecv -  (long) stop_str_len] = '\0';
dmitriy.gerasimov's avatar
dmitriy.gerasimov committed
                *dwLen =(int) nRecv - (int) stop_str_len;
dmitriy.gerasimov's avatar
dmitriy.gerasimov committed
                *dwLen = (int) nRecv;
        char * l_buf_realloc = DAP_REALLOC(lpszBuffer,(size_t) *dwLen + 1);
        if( l_buf_realloc)
            lpszBuffer = l_buf_realloc;
ANTA's avatar
ANTA committed

    // in case of an error or missing string
ANTA's avatar
ANTA committed

ANTA's avatar
ANTA committed

ANTA's avatar
ANTA committed

/**
 * threading function for processing a request from a client
 */
static void* thread_one_client_func(void *args)
{
SOCKET  newsockfd = (SOCKET) (intptr_t) args;
int     str_len, marker = 0, timeout = 5000, argc = 0, is_data;
dap_list_t *cmd_param_list = NULL;
char    *str_header;

    if(s_debug_cli)
Roman Khlopkov's avatar
Roman Khlopkov committed
        log_it(L_DEBUG, "new connection sockfd=%"DAP_FORMAT_SOCKET, newsockfd);

    while ( !(0 > (is_data = s_poll(newsockfd, timeout))) )                 // wait data from client
        if ( !(is_data) )                                                   // timeout
        if ( !is_valid_socket(newsockfd) )
        // receiving http header
        if ( !(str_header = s_get_next_str(newsockfd, &str_len, "\r\n", true, timeout)) )
            break;                                                          // bad format

        if(str_header && !strlen(str_header) ) {
            if(marker == 1){
                DAP_DELETE(str_header);
        // filling parameters of command
            cmd_param_list = dap_list_append(cmd_param_list, str_header);
            //printf("g_list_append argc=%d command=%s ", argc, str_header);
            DAP_DEL_Z(str_header);
        if(marker == 2 &&  cmd_param_list) {
            dap_list_t *list = cmd_param_list;
            argc = dap_list_length(list);
            if(argc >= 1) {
ANTA's avatar
ANTA committed
              int l_verbose = 0;
                list = dap_list_next(list);
                char *str_cmd = dap_strdup_printf("%s", cmd_name);
dmitriy.gerasimov's avatar
dmitriy.gerasimov committed
                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) {
                        char *str_cmd_prev = str_cmd;
                        str_cmd = dap_strdup_printf("%s;%s", str_cmd, list->data);
                        list = dap_list_next(list);
                        DAP_DELETE(str_cmd_prev);
                    if(l_cmd->overrides.log_cmd_call)
                        l_cmd->overrides.log_cmd_call(str_cmd);
                    else
Constantin P.'s avatar
Constantin P. committed
                        log_it(L_DEBUG, "execute command=%s", str_cmd);
                    // exec command
dmitriy.gerasimov's avatar
dmitriy.gerasimov committed
                    char **l_argv = dap_strsplit(str_cmd, ";", -1);
                    // Call the command function
Roman Khlopkov's avatar
Roman Khlopkov committed
                    if(l_cmd &&  l_argv && l_cmd->func) {
                        if (l_cmd->arg_func) {
                            res = l_cmd->func_ex(argc, l_argv, l_cmd->arg_func, &str_reply);
                        } else {
                            res = l_cmd->func(argc, l_argv, &str_reply);
                        }
                    } else if (l_cmd) {
dmitriy.gerasimov's avatar
dmitriy.gerasimov committed
                        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
dmitriy.gerasimov's avatar
dmitriy.gerasimov committed
                    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)
ANTA's avatar
ANTA committed
                  reply_body = dap_strdup_printf("%d\r\nret_code: %d\r\n%s\r\n", res, res, (str_reply) ? str_reply : "");
ANTA's avatar
ANTA committed
                  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);
                size_t l_reply_step = 32768;
                size_t l_reply_len = strlen(reply_str);
                size_t l_reply_rest = l_reply_len;
                while(l_reply_rest) {
                    size_t l_send_bytes = min(l_reply_step, l_reply_rest);
                    int ret = send(newsockfd, reply_str + l_reply_len - l_reply_rest, l_send_bytes, 0);
                    if(ret<=0)
                        break;
                    l_reply_rest-=l_send_bytes;
                };
                DAP_DELETE(str_reply);
                DAP_DELETE(reply_str);
                DAP_DELETE(reply_body);
                DAP_DELETE(str_cmd);
            dap_list_free_full(cmd_param_list, free);
    // close connection
    int cs = closesocket(newsockfd);
    if (s_debug_cli)
Roman Khlopkov's avatar
Roman Khlopkov committed
        log_it(L_DEBUG, "close connection=%d sockfd=%"DAP_FORMAT_SOCKET, cs, newsockfd);
ANTA's avatar
ANTA committed
#ifdef _WIN32

cellframe.docs's avatar
cellframe.docs committed
/**
 * @brief p_get_next_str
 *
 * @param hPipe
 * @param dwLen
 * @param stop_str
 * @param del_stop_str
 * @param timeout
 * @return char*
cellframe.docs's avatar
cellframe.docs committed
 */
ANTA's avatar
ANTA committed
char *p_get_next_str( HANDLE hPipe, int *dwLen, const char *stop_str, bool del_stop_str, int timeout )
{
Roman Khlopkov's avatar
Roman Khlopkov committed
    UNUSED(timeout);
ANTA's avatar
ANTA committed
    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;
}

/**
cellframe.docs's avatar
cellframe.docs committed
 * @brief thread_pipe_client_func
ANTA's avatar
ANTA committed
 * threading function for processing a request from a client
 * @param args
 * @return void*
ANTA's avatar
ANTA committed
 */
static void *thread_pipe_client_func( void *args )
{
    HANDLE hPipe = (HANDLE)args;

//    SOCKET newsockfd = (SOCKET) (intptr_t) args;
    if(s_debug_cli)
Roman Khlopkov's avatar
Roman Khlopkov committed
        log_it(L_INFO, "new connection pipe = %p", hPipe);
ANTA's avatar
ANTA committed

    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

Roman Khlopkov's avatar
Roman Khlopkov committed
                    if ( l_cmd &&  l_argv && l_cmd->func ) {
                        if (l_cmd->arg_func) {
                            res = l_cmd->func_ex(argc, l_argv, l_cmd->arg_func, &str_reply);
                        } else {
                            res = l_cmd->func(argc, l_argv, &str_reply);
                        }
                    }
ANTA's avatar
ANTA committed

                    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);

Roman Khlopkov's avatar
Roman Khlopkov committed
    log_it( L_INFO, "close connection pipe = %p", hPipe );
ANTA's avatar
ANTA committed

Roman Khlopkov's avatar
Roman Khlopkov committed
    FlushFileBuffers( hPipe );
    DisconnectNamedPipe( hPipe );
    CloseHandle( hPipe );
ANTA's avatar
ANTA committed

    return NULL;
}


/**
cellframe.docs's avatar
cellframe.docs committed
 * @brief thread_pipe_func
ANTA's avatar
ANTA committed
 * main threading server function pipe win32
 * @param args
 * @return void*
ANTA's avatar
ANTA committed
 */
static void* thread_pipe_func( void *args )
{
Roman Khlopkov's avatar
Roman Khlopkov committed
   UNUSED(args);
Roman Khlopkov's avatar
Roman Khlopkov committed
   BOOL   fConnected = FALSE;
ANTA's avatar
ANTA committed
   pthread_t threadId;
Roman Khlopkov's avatar
Roman Khlopkov committed
   HANDLE hPipe = INVALID_HANDLE_VALUE;
Roman Khlopkov's avatar
Roman Khlopkov committed
   static const char *cPipeName = "\\\\.\\pipe\\node_cli.pipe";
ANTA's avatar
ANTA committed

Roman Khlopkov's avatar
Roman Khlopkov committed
   for (;;)
   {
ANTA's avatar
ANTA committed
///      printf( "\nPipe Server: Main thread awaiting client connection on %s\n", lpszPipename );

Roman Khlopkov's avatar
Roman Khlopkov committed
      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
ANTA's avatar
ANTA committed

      if ( hPipe == INVALID_HANDLE_VALUE ) {
Roman Khlopkov's avatar
Roman Khlopkov committed
          log_it( L_ERROR, "CreateNamedPipe failed, GLE = %lu.\n", GetLastError() );
ANTA's avatar
ANTA committed
          return NULL;
      }
Roman Khlopkov's avatar
Roman Khlopkov committed

      fConnected = ConnectNamedPipe( hPipe, NULL ) ? TRUE : ( GetLastError() == ERROR_PIPE_CONNECTED );

ANTA's avatar
ANTA committed
      if ( fConnected )
Roman Khlopkov's avatar
Roman Khlopkov committed
      {
Roman Khlopkov's avatar
Roman Khlopkov committed
        log_it( L_INFO, "Client %p connected, creating a processing thread.\n", hPipe );
ANTA's avatar
ANTA committed

        pthread_create( &threadId, NULL, thread_pipe_client_func, hPipe );
        pthread_detach( threadId );
Roman Khlopkov's avatar
Roman Khlopkov committed
      }
      else
ANTA's avatar
ANTA committed
         CloseHandle( hPipe );
Roman Khlopkov's avatar
Roman Khlopkov committed
    }
ANTA's avatar
ANTA committed

    return NULL;
}
#endif

cellframe.doc's avatar
cellframe.doc committed

cellframe.docs's avatar
cellframe.docs committed
 * @brief thread_main_func
 * main threading server function
 * @param args
 * @return void*
 */
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") );
    // wait of clients
    while(1)
    {
        pthread_t threadId;
        struct sockaddr_in peer;
        socklen_t size = sizeof(peer);
        // received a new connection request
        if((newsockfd = accept(sockfd, (struct sockaddr*) &peer, &size)) == (SOCKET) -1) {
Roman Khlopkov's avatar
Roman Khlopkov committed
            log_it(L_ERROR, "new connection break newsockfd=%"DAP_FORMAT_SOCKET, newsockfd);
        // 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
    // close connection
    int cs = closesocket(sockfd);
Roman Khlopkov's avatar
Roman Khlopkov committed
    log_it(L_INFO, "Exit server thread=%d socket=%"DAP_FORMAT_SOCKET, cs, sockfd);
cellframe.doc's avatar
cellframe.doc committed

dmitriy.gerasimov's avatar
dmitriy.gerasimov committed
/**
cellframe.docs's avatar
cellframe.docs committed
 * @brief dap_chain_node_cli_set_reply_text
dmitriy.gerasimov's avatar
dmitriy.gerasimov committed
 * Write text to reply string
 * @param str_reply
 * @param str
 * @param ...
dmitriy.gerasimov's avatar
dmitriy.gerasimov committed
 */
void dap_chain_node_cli_set_reply_text(char **str_reply, const char *str, ...)
{
    if(str_reply) {
        if(*str_reply) {
            assert(! *str_reply );
dmitriy.gerasimov's avatar
dmitriy.gerasimov committed
            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);
    }
}

/**
 * @brief dap_chain_node_cli_check_option
 * @param argv
 * @param arg_start
 * @param arg_end
 * @param opt_name
 * @return
 */
int dap_chain_node_cli_check_option( char** argv, int arg_start, int arg_end, const char *opt_name)
{
    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;
        // find opt_name
        if(arg_string && opt_name && arg_string[0] && opt_name[0] && !strcmp(arg_string, opt_name)) {
                return arg_index;
        }
        arg_index++;
    }
    return -1;
}

cellframe.doc's avatar
cellframe.doc committed

dmitriy.gerasimov's avatar
dmitriy.gerasimov committed
/**
cellframe.docs's avatar
cellframe.docs committed
 * @brief dap_chain_node_cli_find_option_val
dmitriy.gerasimov's avatar
dmitriy.gerasimov committed
 * return index of string in argv, or 0 if not found
 * @param argv
 * @param arg_start
 * @param arg_end
 * @param opt_name
 * @param opt_value
 * @return int
dmitriy.gerasimov's avatar
dmitriy.gerasimov committed
int dap_chain_node_cli_find_option_val( char** argv, int arg_start, int arg_end, const char *opt_name, const char **opt_value)
dmitriy.gerasimov's avatar
dmitriy.gerasimov committed
    int arg_index = arg_start;
    const char *arg_string;
    int l_ret_pos = 0;
dmitriy.gerasimov's avatar
dmitriy.gerasimov committed

    while(arg_index < arg_end)
    {
dmitriy.gerasimov's avatar
dmitriy.gerasimov committed
        char * l_argv_cur = argv[arg_index];
        arg_string = l_argv_cur;
dmitriy.gerasimov's avatar
dmitriy.gerasimov committed
        // find opt_name
dmitriy.gerasimov's avatar
dmitriy.gerasimov committed
        if(arg_string && opt_name && arg_string[0] && opt_name[0] && !strcmp(arg_string, opt_name)) {
dmitriy.gerasimov's avatar
dmitriy.gerasimov committed
            // find opt_value
            if(opt_value) {
                arg_string = argv[++arg_index];
                if(arg_string) {
                    *opt_value = arg_string;
                    return arg_index;
                }
                // for case if opt_name exist without value
                else
                    l_ret_pos = arg_index;
dmitriy.gerasimov's avatar
dmitriy.gerasimov committed
            }
            else
                // need only opt_name
                return arg_index;
        }
        arg_index++;
    }
    return l_ret_pos;
dmitriy.gerasimov's avatar
dmitriy.gerasimov committed
}

/**
 * @brief s_cmd_item_create
 * @param a_name
 * @param func
dmitriy.gerasimov's avatar
dmitriy.gerasimov committed
 * @param doc
 * @param doc_ex
 * @return
 */
Roman Khlopkov's avatar
Roman Khlopkov committed
void dap_chain_node_cli_cmd_item_create_ex(const char * a_name, cmdfunc_ex_t *a_func, void *a_arg_func, const char *a_doc, const char *a_doc_ex)
dmitriy.gerasimov's avatar
dmitriy.gerasimov committed
{
    dap_chain_node_cmd_item_t *l_cmd_item = DAP_NEW_Z(dap_chain_node_cmd_item_t);
ANTA's avatar
ANTA committed
    dap_snprintf(l_cmd_item->name,sizeof (l_cmd_item->name),"%s",a_name);
dmitriy.gerasimov's avatar
dmitriy.gerasimov committed
    l_cmd_item->doc = strdup( a_doc);
    l_cmd_item->doc_ex = strdup( a_doc_ex);
Roman Khlopkov's avatar
Roman Khlopkov committed
    if (a_arg_func) {
        l_cmd_item->func_ex = a_func;
        l_cmd_item->arg_func = a_arg_func;
    } else {
        l_cmd_item->func = (cmdfunc_t *)(void *)a_func;
    }
dmitriy.gerasimov's avatar
dmitriy.gerasimov committed
    HASH_ADD_STR(s_commands,name,l_cmd_item);
    log_it(L_DEBUG,"Added command %s",l_cmd_item->name);
}

cellframe.docs's avatar
cellframe.docs committed
/**
 * @brief dap_chain_node_cli_cmd_item_apply_overrides
 *
 * @param a_name
 * @param a_overrides
cellframe.docs's avatar
cellframe.docs committed
 */
void dap_chain_node_cli_cmd_item_apply_overrides(const char * a_name, const dap_chain_node_cmd_item_func_overrides_t * a_overrides){
    dap_chain_node_cmd_item_t *l_cmd_item = dap_chain_node_cli_cmd_find(a_name);
    if(l_cmd_item)
        l_cmd_item->overrides = *a_overrides;
}

dmitriy.gerasimov's avatar
dmitriy.gerasimov committed
/**
 * @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;
}


cellframe.docs's avatar
cellframe.docs committed
 * @brief dap_chain_node_cli_init
 * Initialization of the server side of the interaction
 * with the console kelvin-node-cli
cellframe.docs's avatar
cellframe.docs committed
 * init commands description
 * @param g_config
 * @return int
 */
int dap_chain_node_cli_init(dap_config_t * g_config)
{
    s_debug_cli = dap_config_get_item_bool_default(g_config,"conserver","debug_cli",false);
ANTA's avatar
ANTA committed
#ifndef _WIN32
    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") );
Station's avatar
Station committed
#else
   pthread_t threadId;
ANTA's avatar
ANTA committed
#endif
Station's avatar
Station committed

ANTA's avatar
ANTA committed
    struct sockaddr_in server_addr;
    SOCKET sockfd = -1;

    bool l_conserver_enabled = dap_config_get_item_bool_default( g_config, "conserver", "enabled", true );
ANTA's avatar
ANTA committed

    if ( !l_conserver_enabled ) {
ANTA's avatar
ANTA committed

        log_it( L_WARNING, "Console Server is dissabled." );
        return 0;
    }

Constantin Papizh's avatar
Constantin Papizh committed
#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

Roman Khlopkov's avatar
Roman Khlopkov committed
    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"
            );
Dmitriy Naidolinskiy's avatar
Dmitriy Naidolinskiy committed
    dap_chain_node_cli_cmd_item_create("mempool", com_signer, "Sign operations",
               "mempool sign -cert <cert name> -net <net name> -chain <chain name> -file <filename> [-mime {<SIGNER_FILENAME,SIGNER_FILENAME_SHORT,SIGNER_FILESIZE,SIGNER_DATE,SIGNER_MIME_MAGIC> | <SIGNER_ALL_FLAGS>}]\n"
               "mempool check -cert <cert name> -net <net name> {-file <filename> | -hash <hash>} [-mime {<SIGNER_FILENAME,SIGNER_FILENAME_SHORT,SIGNER_FILESIZE,SIGNER_DATE,SIGNER_MIME_MAGIC> | <SIGNER_ALL_FLAGS>}]\n"
Dmitriy Naidolinskiy's avatar
Dmitriy Naidolinskiy committed
                                          );
Roman Khlopkov's avatar
Roman Khlopkov committed
    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 -net <net name> {-addr <node address> | -alias <node alias> | auto}\n\n"
                    "node handshake -net <net name> {-addr <node address> | -alias <node alias>}\n"
                    "node dump -net <net name> [ -addr <node address> | -alias <node alias>] [-full]\n\n"
Roman Khlopkov's avatar
Roman Khlopkov committed
    dap_chain_node_cli_cmd_item_create ("ping", com_ping, "Send ICMP ECHO_REQUEST to network hosts",
            "ping [-c <count>] host\n");
Roman Khlopkov's avatar
Roman Khlopkov committed
    dap_chain_node_cli_cmd_item_create ("traceroute", com_traceroute, "Print the hops and time of packets trace to network host",
            "traceroute host\n");
Roman Khlopkov's avatar
Roman Khlopkov committed
    dap_chain_node_cli_cmd_item_create ("tracepath", com_tracepath,"Traces path to a network host along this path",
            "tracepath host\n");
Roman Khlopkov's avatar
Roman Khlopkov committed
    dap_chain_node_cli_cmd_item_create ("version", com_version, "Return software version",
                                        "version\n"
                                        "\tReturn version number\n"
                                        );

Roman Khlopkov's avatar
Roman Khlopkov committed
    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"
                                        );
Roman Khlopkov's avatar
Roman Khlopkov committed
    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"
                                        );
Roman Khlopkov's avatar
Roman Khlopkov committed
    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
Roman Khlopkov's avatar
Roman Khlopkov committed
    dap_chain_node_cli_cmd_item_create ("token_update", com_token_update, "Token update",
            "\nPrivate token update\n"
Roman Khlopkov's avatar
Roman Khlopkov committed
            "\t token_update -net <net name> -chain <chain name> -token <token ticker> [-type private] [-<Param name 1> <Param Value 1>] [-Param name 2> <Param Value 2>] ...[-<Param Name N> <Param Value N>]\n"
            "\t   Update private token <token ticker> for <netname>:<chain name> with"
            "\t   custom parameters list <Param 1>, <Param 2>...<Param N>."
            "\n"
            "==Params==\n"
            "General:\n"
            "\t -flags_set <value>:\t Set list of flags from <value> to token declaration\n"
            "\t -flags_unset <value>:\t Unset list of flags from <value> from token declaration\n"
            "\t -total_supply <value>:\t Set total supply - emission's maximum - to the <value>\n"
            "\t -total_signs_valid <value>:\t Set valid signatures count's minimum\n"
            "\t -signs_add <value>:\t Add signature's pkey fingerprint to the list of owners\n"
            "\t -signs_remove <value>:\t Remove signature's pkey fingerprint from the owners\n"
            "\nDatum type allowed/blocked updates:\n"
            "\t -datum_type_allowed_add <value>:\t Add allowed datum type(s)\n"
            "\t -datum_type_allowed_remove <value>:\t Remove datum type(s) from allowed\n"
            "\t -datum_type_allowed_clear:\t Remove all datum types from allowed\n"
            "\t -datum_type_blocked_add <value>:\t Add blocked datum type(s)\n"
            "\t -datum_type_blocked_remove <value>:\t Remove datum type(s) from blocked\n"
            "\t -datum_type_blocked_clear:\t Remove all datum types from blocked\n"
            "\nTx receiver addresses allowed/blocked updates:\n"
            "\t -tx_receiver_allowed_add <value>:\t Add allowed tx receiver(s)\n"
            "\t -tx_receiver_allowed_remove <value>:\t Remove tx receiver(s) from allowed\n"
            "\t -tx_receiver_allowed_clear:\t Remove all tx receivers from allowed\n"
            "\t -tx_receiver_blocked_add <value>:\t Add blocked tx receiver(s)\n"
            "\t -tx_receiver_blocked_remove <value>:\t Remove tx receiver(s) from blocked\n"
            "\t -tx_receiver_blocked_clear:\t Remove all tx receivers from blocked\n"
            "\nTx sender addresses allowed/blocked updates:\n"
            "\t -tx_sender_allowed_add <value>:\t Add allowed tx sender(s)\n"
            "\t -tx_sender_allowed_remove <value>:\t Remove tx sender(s) from allowed\n"
            "\t -tx_sender_allowed_clear:\t Remove all tx senders from allowed\n"
            "\t -tx_sender_blocked_add <value>:\t Add allowed tx sender(s)\n"
            "\t -tx_sender_blocked_remove <value>:\t Remove tx sender(s) from blocked\n"
            "\t -tx_sender_blocked_clear:\t Remove all tx sender(s) from blocked\n"
            "\n"
            "==Flags=="
            "\t ALL_BLOCKED:\t Blocked all permissions, usefull add it first and then add allows what you want to allow\n"
            "\t ALL_ALLOWED:\t Allowed all permissions if not blocked them. Be careful with this mode\n"
            "\t ALL_FROZEN:\t All permissions are temprorary frozen\n"
            "\t ALL_UNFROZEN:\t Unfrozen permissions\n"
            "\t STATIC_ALL:\t No token manipulations after declarations at all. Token declares staticly and can't variabed after\n"
            "\t STATIC_FLAGS:\t No token manipulations after declarations with flags\n"
            "\t STATIC_PERMISSIONS_ALL:\t No all permissions lists manipulations after declarations\n"
            "\t STATIC_PERMISSIONS_DATUM_TYPE:\t No datum type permissions lists manipulations after declarations\n"
            "\t STATIC_PERMISSIONS_TX_SENDER:\t No tx sender permissions lists manipulations after declarations\n"
            "\t STATIC_PERMISSIONS_TX_RECEIVER:\t No tx receiver permissions lists manipulations after declarations\n"