client.cpp 6.0 KB
Newer Older
羽飞's avatar
羽飞 已提交
1
/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved.
羽飞's avatar
羽飞 已提交
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
miniob is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
         http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details. */

//
// Created by Longda on 2021
//

#include <arpa/inet.h>
#include <errno.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>
#include <termios.h>
羽飞's avatar
羽飞 已提交
28
#include <time.h>
羽飞's avatar
羽飞 已提交
29 30 31 32

#include "common/defs.h"
#include "common/lang/string.h"

羽飞's avatar
羽飞 已提交
33 34
#ifdef USE_READLINE
#include "readline/readline.h"
羽飞's avatar
羽飞 已提交
35
#include "readline/history.h"
羽飞's avatar
羽飞 已提交
36 37
#endif

羽飞's avatar
羽飞 已提交
38 39 40 41 42
#define MAX_MEM_BUFFER_SIZE 8192
#define PORT_DEFAULT 6789

using namespace common;

羽飞's avatar
羽飞 已提交
43
#ifdef USE_READLINE
羽飞's avatar
羽飞 已提交
44
const std::string HISTORY_FILE = std::string(getenv("HOME")) + "/.miniob.history";
羽飞's avatar
羽飞 已提交
45
time_t last_history_write_time = 0;
羽飞's avatar
羽飞 已提交
46

羽飞's avatar
羽飞 已提交
47 48
char *my_readline(const char *prompt) 
{
羽飞's avatar
羽飞 已提交
49 50 51 52 53 54 55 56 57 58 59 60 61
  int size = history_length;
  if (size == 0) {
    read_history(HISTORY_FILE.c_str());

    FILE *fp = fopen(HISTORY_FILE.c_str(), "a");
    if (fp != nullptr) {
      fclose(fp);
    }
  }

  char *line = readline(prompt);
  if (line != nullptr && line[0] != 0) {
    add_history(line);
羽飞's avatar
羽飞 已提交
62 63 64 65 66
    if (time(NULL) - last_history_write_time > 5) {
      write_history(HISTORY_FILE.c_str());
    }
    // append_history doesn't work on some readlines
    // append_history(1, HISTORY_FILE.c_str());
羽飞's avatar
羽飞 已提交
67 68
  }
  return line;
羽飞's avatar
羽飞 已提交
69 70 71 72 73 74 75 76 77
}
#else // USE_READLINE
char *my_readline(const char *prompt)
{
  char *buffer = (char *)malloc(MAX_MEM_BUFFER_SIZE);
  if (nullptr == buffer) {
    fprintf(stderr, "failed to alloc line buffer");
    return nullptr;
  }
78
  fprintf(stdout, "%s", prompt);
羽飞's avatar
羽飞 已提交
79 80 81 82 83 84 85 86 87 88
  char *s = fgets(buffer, MAX_MEM_BUFFER_SIZE, stdin);
  if (nullptr == s) {
    fprintf(stderr, "failed to read message from console");
    free(buffer);
    return nullptr;
  }
  return buffer;
}
#endif // USE_READLINE

阿福Chris's avatar
阿福Chris 已提交
89 90 91 92
/* this function config a exit-cmd list, strncasecmp func truncate the command from terminal according to the number,
   'strncasecmp("exit", cmd, 4)' means that obclient read command string from terminal, truncate it to 4 chars from 
   the beginning, then compare the result with 'exit', if they match, exit the obclient.
*/
羽飞's avatar
羽飞 已提交
93 94
bool is_exit_command(const char *cmd) {
  return 0 == strncasecmp("exit", cmd, 4) ||
阿福Chris's avatar
阿福Chris 已提交
95
         0 == strncasecmp("bye", cmd, 3) ||
阿福Chris's avatar
阿福Chris 已提交
96
         0 == strncasecmp("\\q", cmd, 2) ;
羽飞's avatar
羽飞 已提交
97 98
}

99 100
int init_unix_sock(const char *unix_sock_path)
{
羽飞's avatar
羽飞 已提交
101 102 103 104 105 106 107 108 109 110 111 112
  int sockfd = socket(PF_UNIX, SOCK_STREAM, 0);
  if (sockfd < 0) {
    fprintf(stderr, "failed to create unix socket. %s", strerror(errno));
    return -1;
  }

  struct sockaddr_un sockaddr;
  memset(&sockaddr, 0, sizeof(sockaddr));
  sockaddr.sun_family = PF_UNIX;
  snprintf(sockaddr.sun_path, sizeof(sockaddr.sun_path), "%s", unix_sock_path);

  if (connect(sockfd, (struct sockaddr *)&sockaddr, sizeof(sockaddr)) < 0) {
113
    fprintf(stderr, "failed to connect to server. unix socket path '%s'. error %s", sockaddr.sun_path, strerror(errno));
羽飞's avatar
羽飞 已提交
114 115 116 117 118 119
    close(sockfd);
    return -1;
  }
  return sockfd;
}

120 121
int init_tcp_sock(const char *server_host, int server_port)
{
羽飞's avatar
羽飞 已提交
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
  struct hostent *host;
  struct sockaddr_in serv_addr;

  if ((host = gethostbyname(server_host)) == NULL) {
    fprintf(stderr, "gethostbyname failed. errmsg=%d:%s\n", errno, strerror(errno));
    return -1;
  }

  int sockfd;
  if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
    fprintf(stderr, "create socket error. errmsg=%d:%s\n", errno, strerror(errno));
    return -1;
  }

  serv_addr.sin_family = AF_INET;
  serv_addr.sin_port = htons(server_port);
  serv_addr.sin_addr = *((struct in_addr *)host->h_addr);
  bzero(&(serv_addr.sin_zero), 8);

141
  if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr)) == -1) {
羽飞's avatar
羽飞 已提交
142 143 144 145 146 147 148
    fprintf(stderr, "Failed to connect. errmsg=%d:%s\n", errno, strerror(errno));
    close(sockfd);
    return -1;
  }
  return sockfd;
}

149 150
int main(int argc, char *argv[])
{
羽飞's avatar
羽飞 已提交
151 152 153 154 155 156 157
  const char *unix_socket_path = nullptr;
  const char *server_host = "127.0.0.1";
  int server_port = PORT_DEFAULT;
  int opt;
  extern char *optarg;
  while ((opt = getopt(argc, argv, "s:h:p:")) > 0) {
    switch (opt) {
158 159 160 161 162 163 164 165 166
      case 's':
        unix_socket_path = optarg;
        break;
      case 'p':
        server_port = atoi(optarg);
        break;
      case 'h':
        server_host = optarg;
        break;
羽飞's avatar
羽飞 已提交
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
    }
  }

  const char *prompt_str = "miniob > ";

  int sockfd, send_bytes;

  if (unix_socket_path != nullptr) {
    sockfd = init_unix_sock(unix_socket_path);
  } else {
    sockfd = init_tcp_sock(server_host, server_port);
  }
  if (sockfd < 0) {
    return 1;
  }

  char send_buf[MAX_MEM_BUFFER_SIZE];

羽飞's avatar
羽飞 已提交
185 186 187 188
  char *input_command = nullptr;
  while ((input_command = my_readline(prompt_str)) != nullptr) {
    if (common::is_blank(input_command)) {
      free(input_command);
羽飞's avatar
羽飞 已提交
189 190 191
      continue;
    }

羽飞's avatar
羽飞 已提交
192 193
    if (is_exit_command(input_command)) {
      free(input_command);
羽飞's avatar
羽飞 已提交
194 195 196
      break;
    }

羽飞's avatar
羽飞 已提交
197
    if ((send_bytes = write(sockfd, input_command, strlen(input_command) + 1)) == -1) { // TODO writen
羽飞's avatar
羽飞 已提交
198 199 200
      fprintf(stderr, "send error: %d:%s \n", errno, strerror(errno));
      exit(1);
    }
羽飞's avatar
羽飞 已提交
201
    free(input_command);
羽飞's avatar
羽飞 已提交
202 203 204
    memset(send_buf, 0, sizeof(send_buf));

    int len = 0;
205
    while ((len = recv(sockfd, send_buf, MAX_MEM_BUFFER_SIZE, 0)) > 0) {
羽飞's avatar
羽飞 已提交
206 207 208 209 210
      bool msg_end = false;
      for (int i = 0; i < len; i++) {
        if (0 == send_buf[i]) {
          msg_end = true;
          break;
211
        }
羽飞's avatar
羽飞 已提交
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
        printf("%c", send_buf[i]);
      }
      if (msg_end) {
        break;
      }
      memset(send_buf, 0, MAX_MEM_BUFFER_SIZE);
    }

    if (len < 0) {
      fprintf(stderr, "Connection was broken: %s\n", strerror(errno));
      break;
    }
    if (0 == len) {
      printf("Connection has been closed\n");
      break;
    }
  }
  close(sockfd);

  return 0;
}