shellArguments.c 14.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/*
 * Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
 *
 * This program is free software: you can use, redistribute, and/or modify
 * it under the terms of the GNU Affero General Public License, version 3
 * or later ("AGPL"), as published by the Free Software Foundation.
 *
 * This program 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.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

wafwerar's avatar
wafwerar 已提交
16 17 18 19
#ifdef _TD_DARWIN_64
#include <pwd.h>
#endif

20
#include "shellInt.h"
21
#include "version.h"
22

sangshuduo's avatar
sangshuduo 已提交
23
#ifndef CUS_NAME
24
char cusName[] = "TDengine";
sangshuduo's avatar
sangshuduo 已提交
25 26
#endif

sangshuduo's avatar
sangshuduo 已提交
27
#ifndef CUS_PROMPT
28
char cusPrompt[] = "taos";
sangshuduo's avatar
sangshuduo 已提交
29 30
#endif

sangshuduo's avatar
sangshuduo 已提交
31
#ifndef CUS_EMAIL
32
char cusEmail[] = "<support@taosdata.com>";
sangshuduo's avatar
sangshuduo 已提交
33 34
#endif

sangshuduo's avatar
sangshuduo 已提交
35
#if defined(CUS_NAME) || defined(CUS_PROMPT) || defined(CUS_EMAIL)
sangshuduo's avatar
sangshuduo 已提交
36
#include "cus_name.h"
sangshuduo's avatar
sangshuduo 已提交
37 38
#endif

39 40
#define TAOS_CONSOLE_PROMPT_CONTINUE "   -> "

sangshuduo's avatar
sangshuduo 已提交
41
#define SHELL_HOST     "The server FQDN to connect. The default host is localhost."
42 43 44 45 46 47 48 49 50 51 52 53 54
#define SHELL_PORT     "The TCP/IP port number to use for the connection."
#define SHELL_USER     "The user name to use when connecting to the server."
#define SHELL_PASSWORD "The password to use when connecting to the server."
#define SHELL_AUTH     "The auth string to use when connecting to the server."
#define SHELL_GEN_AUTH "Generate auth string from password."
#define SHELL_CFG_DIR  "Configuration directory."
#define SHELL_DMP_CFG  "Dump configuration."
#define SHELL_CMD      "Commands to run without enter the shell."
#define SHELL_RAW_TIME "Output time as uint64_t."
#define SHELL_FILE     "Script to run without enter the shell."
#define SHELL_DB       "Database to use when connecting to the server."
#define SHELL_CHECK    "Check the service status."
#define SHELL_STARTUP  "Check the details of the service status."
55
#define SHELL_WIDTH    "Set the default binary display width, default is 30."
56
#define SHELL_NET_ROLE "Net role when network connectivity test, options: client|server."
wafwerar's avatar
wafwerar 已提交
57
#define SHELL_PKT_LEN  "Packet length used for net test, default is 1024 bytes."
58 59
#define SHELL_PKT_NUM  "Packet numbers used for net test, default is 100."
#define SHELL_VERSION  "Print program version."
60

Y
Yang Zhao 已提交
61
#ifdef WEBSOCKET
62 63
#define SHELL_DSN     "Use dsn to connect to the cloud server or to a remote server which provides WebSocket connection."
#define SHELL_REST    "Use RESTful mode when connecting."
64
#define SHELL_TIMEOUT "Set the timeout for websocket query in seconds, default is 30."
Y
Yang Zhao 已提交
65 66
#endif

wafwerar's avatar
wafwerar 已提交
67 68
static int32_t shellParseSingleOpt(int32_t key, char *arg);

69 70
void shellPrintHelp() {
  char indent[] = "  ";
wafwerar's avatar
wafwerar 已提交
71 72 73 74 75 76 77 78 79
  printf("Usage: taos [OPTION...] \r\n\r\n");
  printf("%s%s%s%s\r\n", indent, "-a,", indent, SHELL_AUTH);
  printf("%s%s%s%s\r\n", indent, "-A,", indent, SHELL_GEN_AUTH);
  printf("%s%s%s%s\r\n", indent, "-c,", indent, SHELL_CFG_DIR);
  printf("%s%s%s%s\r\n", indent, "-C,", indent, SHELL_DMP_CFG);
  printf("%s%s%s%s\r\n", indent, "-d,", indent, SHELL_DB);
  printf("%s%s%s%s\r\n", indent, "-f,", indent, SHELL_FILE);
  printf("%s%s%s%s\r\n", indent, "-h,", indent, SHELL_HOST);
  printf("%s%s%s%s\r\n", indent, "-k,", indent, SHELL_CHECK);
wafwerar's avatar
wafwerar 已提交
80
  printf("%s%s%s%s\r\n", indent, "-l,", indent, SHELL_PKT_LEN);
wafwerar's avatar
wafwerar 已提交
81 82 83 84 85 86 87 88
  printf("%s%s%s%s\r\n", indent, "-n,", indent, SHELL_NET_ROLE);
  printf("%s%s%s%s\r\n", indent, "-N,", indent, SHELL_PKT_NUM);
  printf("%s%s%s%s\r\n", indent, "-p,", indent, SHELL_PASSWORD);
  printf("%s%s%s%s\r\n", indent, "-P,", indent, SHELL_PORT);
  printf("%s%s%s%s\r\n", indent, "-r,", indent, SHELL_RAW_TIME);
  printf("%s%s%s%s\r\n", indent, "-s,", indent, SHELL_CMD);
  printf("%s%s%s%s\r\n", indent, "-t,", indent, SHELL_STARTUP);
  printf("%s%s%s%s\r\n", indent, "-u,", indent, SHELL_USER);
Y
Yang Zhao 已提交
89 90 91 92 93
#ifdef WEBSOCKET
  printf("%s%s%s%s\r\n", indent, "-E,", indent, SHELL_DSN);
  printf("%s%s%s%s\r\n", indent, "-R,", indent, SHELL_REST);
  printf("%s%s%s%s\r\n", indent, "-T,", indent, SHELL_TIMEOUT);
#endif
wafwerar's avatar
wafwerar 已提交
94 95
  printf("%s%s%s%s\r\n", indent, "-w,", indent, SHELL_WIDTH);
  printf("%s%s%s%s\r\n", indent, "-V,", indent, SHELL_VERSION);
96 97 98 99 100
#ifdef CUS_EMAIL
  printf("\r\n\r\nReport bugs to %s.\r\n", CUS_EMAIL);
#else
  printf("\r\n\r\nReport bugs to %s.\r\n", "support@taosdata.com");
#endif
101 102
}

103 104
#ifdef LINUX
#include <argp.h>
sangshuduo's avatar
sangshuduo 已提交
105 106 107
#ifdef _ALPINE
#include <termios.h>
#else
108
#include <termio.h>
sangshuduo's avatar
sangshuduo 已提交
109
#endif
110 111

const char *argp_program_version = version;
112 113 114 115 116
#ifdef CUS_EMAIL
const char *argp_program_bug_address = CUS_EMAIL;
#else
const char *argp_program_bug_address = "support@taosdata.com";
#endif
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134

static struct argp_option shellOptions[] = {
    {"host", 'h', "HOST", 0, SHELL_HOST},
    {"port", 'P', "PORT", 0, SHELL_PORT},
    {"user", 'u', "USER", 0, SHELL_USER},
    {0, 'p', 0, 0, SHELL_PASSWORD},
    {"auth", 'a', "AUTH", 0, SHELL_AUTH},
    {"generate-auth", 'A', 0, 0, SHELL_GEN_AUTH},
    {"config-dir", 'c', "DIR", 0, SHELL_CFG_DIR},
    {"dump-config", 'C', 0, 0, SHELL_DMP_CFG},
    {"commands", 's', "COMMANDS", 0, SHELL_CMD},
    {"raw-time", 'r', 0, 0, SHELL_RAW_TIME},
    {"file", 'f', "FILE", 0, SHELL_FILE},
    {"database", 'd', "DATABASE", 0, SHELL_DB},
    {"check", 'k', 0, 0, SHELL_CHECK},
    {"startup", 't', 0, 0, SHELL_STARTUP},
    {"display-width", 'w', "WIDTH", 0, SHELL_WIDTH},
    {"netrole", 'n', "NETROLE", 0, SHELL_NET_ROLE},
wafwerar's avatar
wafwerar 已提交
135
    {"pktlen", 'l', "PKTLEN", 0, SHELL_PKT_LEN},
Y
Yang Zhao 已提交
136 137 138
#ifdef WEBSOCKET
    {"dsn", 'E', "DSN", 0, SHELL_DSN},
    {"restful", 'R', 0, 0, SHELL_REST},
139
    {"timeout", 'T', "SECONDS", 0, SHELL_TIMEOUT},
Y
Yang Zhao 已提交
140
#endif
141 142 143 144
    {"pktnum", 'N', "PKTNUM", 0, SHELL_PKT_NUM},
    {0},
};

wafwerar's avatar
wafwerar 已提交
145 146 147 148 149 150 151 152 153 154 155 156
static error_t shellParseOpt(int32_t key, char *arg, struct argp_state *state) { return shellParseSingleOpt(key, arg); }

static struct argp shellArgp = {shellOptions, shellParseOpt, "", ""};

static void shellParseArgsUseArgp(int argc, char *argv[]) {
  argp_program_version = shell.info.programVersion;
  argp_parse(&shellArgp, argc, argv, 0, 0, &shell.args);
}

#endif

#ifndef ARGP_ERR_UNKNOWN
157
#define ARGP_ERR_UNKNOWN E2BIG
wafwerar's avatar
wafwerar 已提交
158 159
#endif

160 161
static int32_t shellParseSingleOpt(int32_t key, char *arg) {
  SShellArgs *pArgs = &shell.args;
162 163 164

  switch (key) {
    case 'h':
165
      pArgs->host = arg;
Y
Yang Zhao 已提交
166 167 168
#ifdef WEBSOCKET
      pArgs->cloud = false;
#endif
169 170
      break;
    case 'P':
171
      pArgs->port = atoi(arg);
Y
Yang Zhao 已提交
172 173 174
#ifdef WEBSOCKET
      pArgs->cloud = false;
#endif
175
      if (pArgs->port == 0) pArgs->port = -1;
176 177
      break;
    case 'u':
178
      pArgs->user = arg;
179 180 181 182
      break;
    case 'p':
      break;
    case 'a':
183
      pArgs->auth = arg;
184 185
      break;
    case 'A':
186
      pArgs->is_gen_auth = true;
187 188
      break;
    case 'c':
Y
Yang Zhao 已提交
189 190 191
#ifdef WEBSOCKET
      pArgs->cloud = false;
#endif
192
      pArgs->cfgdir = arg;
193 194
      break;
    case 'C':
195
      pArgs->is_dump_config = true;
196 197
      break;
    case 's':
198
      pArgs->commands = arg;
199 200
      break;
    case 'r':
201
      pArgs->is_raw_time = true;
202 203
      break;
    case 'f':
204
      tstrncpy(pArgs->file, arg, sizeof(pArgs->file));
205 206
      break;
    case 'd':
207
      pArgs->database = arg;
208 209
      break;
    case 'k':
210
      pArgs->is_check = true;
211 212
      break;
    case 't':
213
      pArgs->is_startup = true;
214 215
      break;
    case 'w':
216
      pArgs->displayWidth = atoi(arg);
217 218
      break;
    case 'n':
219
      pArgs->netrole = arg;
220 221
      break;
    case 'l':
222
      pArgs->pktLen = atoi(arg);
223 224
      break;
    case 'N':
225
      pArgs->pktNum = atoi(arg);
226
      break;
Y
Yang Zhao 已提交
227 228 229 230 231 232 233 234
#ifdef WEBSOCKET
    case 'R':
      pArgs->restful = true;
      break;
    case 'E':
      pArgs->dsn = arg;
      pArgs->cloud = true;
      break;
235 236 237
    case 'T':
      pArgs->timeout = atoi(arg);
      break;
Y
Yang Zhao 已提交
238
#endif
239
    case 'V':
240 241 242 243
      pArgs->is_version = true;
      break;
    case '?':
      pArgs->is_help = true;
244 245
      break;
    case 1:
246
      pArgs->abort = 1;
247 248 249 250 251 252 253
      break;
    default:
      return ARGP_ERR_UNKNOWN;
  }
  return 0;
}

254 255 256 257
int32_t shellParseArgsWithoutArgp(int argc, char *argv[]) {
  SShellArgs *pArgs = &shell.args;

  for (int i = 1; i < argc; i++) {
258 259
    if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "--usage") == 0 || strcmp(argv[i], "-?") == 0 ||
        strcmp(argv[i], "/?") == 0) {
260 261 262 263 264 265 266
      shellParseSingleOpt('?', NULL);
      return 0;
    }

    char   *key = argv[i];
    int32_t keyLen = strlen(key);
    if (keyLen != 2) {
wafwerar's avatar
wafwerar 已提交
267
      fprintf(stderr, "invalid option %s\r\n", key);
268 269 270
      return -1;
    }
    if (key[0] != '-') {
wafwerar's avatar
wafwerar 已提交
271
      fprintf(stderr, "invalid option %s\r\n", key);
272 273 274
      return -1;
    }

275 276
    if (key[1] == 'h' || key[1] == 'P' || key[1] == 'u' || key[1] == 'a' || key[1] == 'c' || key[1] == 's' ||
        key[1] == 'f' || key[1] == 'd' || key[1] == 'w' || key[1] == 'n' || key[1] == 'l' || key[1] == 'N'
Y
Yang Zhao 已提交
277
#ifdef WEBSOCKET
278
        || key[1] == 'E' || key[1] == 'T'
Y
Yang Zhao 已提交
279
#endif
280
    ) {
281
      if (i + 1 >= argc) {
wafwerar's avatar
wafwerar 已提交
282
        fprintf(stderr, "option %s requires an argument\r\n", key);
283 284 285 286
        return -1;
      }
      char *val = argv[i + 1];
      if (val[0] == '-') {
wafwerar's avatar
wafwerar 已提交
287
        fprintf(stderr, "option %s requires an argument\r\n", key);
288 289 290 291
        return -1;
      }
      shellParseSingleOpt(key[1], val);
      i++;
292 293
    } else if (key[1] == 'p' || key[1] == 'A' || key[1] == 'C' || key[1] == 'r' || key[1] == 'k' || key[1] == 't' ||
               key[1] == 'V' || key[1] == '?' || key[1] == 1
Y
Yang Zhao 已提交
294
#ifdef WEBSOCKET
295
               || key[1] == 'R'
Y
Yang Zhao 已提交
296
#endif
297
    ) {
298 299
      shellParseSingleOpt(key[1], NULL);
    } else {
wafwerar's avatar
wafwerar 已提交
300
      fprintf(stderr, "invalid option %s\r\n", key);
301 302 303 304 305 306 307
      return -1;
    }
  }

  return 0;
}

308 309
static void shellInitArgs(int argc, char *argv[]) {
  for (int i = 1; i < argc; i++) {
310
    if (strncmp(argv[i], "-p", 2) == 0) {
wafwerar's avatar
wafwerar 已提交
311
      // printf(shell.info.clientVersion, taos_get_client_info());
312
      if (strlen(argv[i]) == 2) {
313 314 315 316 317 318 319
        printf("Enter password: ");
        taosSetConsoleEcho(false);
        if (scanf("%20s", shell.args.password) > 1) {
          fprintf(stderr, "password reading error\n");
        }
        taosSetConsoleEcho(true);
        if (EOF == getchar()) {
wafwerar's avatar
wafwerar 已提交
320
          fprintf(stderr, "getchar() return EOF\r\n");
321 322 323 324 325 326 327 328 329 330 331
        }
      } else {
        tstrncpy(shell.args.password, (char *)(argv[i] + 2), sizeof(shell.args.password));
        strcpy(argv[i], "-p");
      }
    }
  }
  if (strlen(shell.args.password) == 0) {
    tstrncpy(shell.args.password, TSDB_DEFAULT_PASS, sizeof(shell.args.password));
  }

332 333 334 335 336
  SShellArgs *pArgs = &shell.args;
  pArgs->user = TSDB_DEFAULT_USER;
  pArgs->pktLen = SHELL_DEF_PKG_LEN;
  pArgs->pktNum = SHELL_DEF_PKG_NUM;
  pArgs->displayWidth = SHELL_DEFAULT_MAX_BINARY_DISPLAY_WIDTH;
337 338
}

339 340 341
static int32_t shellCheckArgs() {
  SShellArgs *pArgs = &shell.args;
  if (pArgs->host != NULL && (strlen(pArgs->host) <= 0 || strlen(pArgs->host) > TSDB_FQDN_LEN)) {
wafwerar's avatar
wafwerar 已提交
342
    printf("Invalid host:%s\r\n", pArgs->host);
343 344 345 346
    return -1;
  }

  if (pArgs->user != NULL && (strlen(pArgs->user) <= 0 || strlen(pArgs->user) > TSDB_USER_LEN)) {
wafwerar's avatar
wafwerar 已提交
347
    printf("Invalid user:%s\r\n", pArgs->user);
348 349 350 351
    return -1;
  }

  if (pArgs->auth != NULL && (strlen(pArgs->auth) <= 0 || strlen(pArgs->auth) > TSDB_PASSWORD_LEN)) {
wafwerar's avatar
wafwerar 已提交
352
    printf("Invalid auth:%s\r\n", pArgs->auth);
353 354 355 356
    return -1;
  }

  if (pArgs->database != NULL && (strlen(pArgs->database) <= 0 || strlen(pArgs->database) > TSDB_DB_NAME_LEN)) {
wafwerar's avatar
wafwerar 已提交
357
    printf("Invalid database:%s\r\n", pArgs->database);
358 359 360
    return -1;
  }

361 362 363 364 365
  if (pArgs->file[0] != 0) {
    char fullname[PATH_MAX] = {0};
    if (taosExpandDir(pArgs->file, fullname, PATH_MAX) == 0) {
      tstrncpy(pArgs->file, fullname, PATH_MAX);
    }
366 367 368 369
  }

  if (pArgs->cfgdir != NULL) {
    if (strlen(pArgs->cfgdir) <= 0 || strlen(pArgs->cfgdir) >= PATH_MAX) {
wafwerar's avatar
wafwerar 已提交
370
      printf("Invalid cfgdir:%s\r\n", pArgs->cfgdir);
371 372
      return -1;
    } else {
373 374 375
      if (taosExpandDir(pArgs->cfgdir, configDir, PATH_MAX) != 0) {
        tstrncpy(configDir, pArgs->cfgdir, PATH_MAX);
      }
376 377 378 379
    }
  }

  if (pArgs->commands != NULL && (strlen(pArgs->commands) <= 0)) {
wafwerar's avatar
wafwerar 已提交
380
    printf("Invalid commands:%s\r\n", pArgs->commands);
381 382 383 384
    return -1;
  }

  if (pArgs->netrole != NULL && !(strcmp(pArgs->netrole, "client") == 0 || strcmp(pArgs->netrole, "server") == 0)) {
wafwerar's avatar
wafwerar 已提交
385
    printf("Invalid netrole:%s\r\n", pArgs->netrole);
386 387 388
    return -1;
  }

S
Shengliang Guan 已提交
389
  if (/*pArgs->password != NULL && */ (strlen(pArgs->password) <= 0)) {
wafwerar's avatar
wafwerar 已提交
390
    printf("Invalid password\r\n");
391 392 393
    return -1;
  }

394
  if (pArgs->port < 0 || pArgs->port > 65535) {
wafwerar's avatar
wafwerar 已提交
395
    printf("Invalid port\r\n");
396 397 398
    return -1;
  }

399
  if (pArgs->pktLen < SHELL_MIN_PKG_LEN || pArgs->pktLen > SHELL_MAX_PKG_LEN) {
wafwerar's avatar
wafwerar 已提交
400
    printf("Invalid pktLen:%d, range:[%d, %d]\r\n", pArgs->pktLen, SHELL_MIN_PKG_LEN, SHELL_MAX_PKG_LEN);
401 402 403
    return -1;
  }

404
  if (pArgs->pktNum < SHELL_MIN_PKG_NUM || pArgs->pktNum > SHELL_MAX_PKG_NUM) {
wafwerar's avatar
wafwerar 已提交
405
    printf("Invalid pktNum:%d, range:[%d, %d]\r\n", pArgs->pktNum, SHELL_MIN_PKG_NUM, SHELL_MAX_PKG_NUM);
406 407 408 409
    return -1;
  }

  if (pArgs->displayWidth <= 0 || pArgs->displayWidth > 10 * 1024) {
wafwerar's avatar
wafwerar 已提交
410
    printf("Invalid displayWidth:%d, range:[1, 10 * 1024]\r\n", pArgs->displayWidth);
411 412 413 414 415
    return -1;
  }

  return 0;
}
416 417 418

int32_t shellParseArgs(int32_t argc, char *argv[]) {
  shellInitArgs(argc, argv);
419
  shell.info.clientVersion =
sangshuduo's avatar
sangshuduo 已提交
420 421
      "Welcome to the %s Command Line Interface, Client Version:%s\r\n"
      "Copyright (c) 2022 by %s, all rights reserved.\r\n\r\n";
422 423 424 425 426
#ifdef CUS_NAME
  strcpy(shell.info.cusName, CUS_NAME);
#else
  strcpy(shell.info.cusName, "TDengine");
#endif
427
  char promptContinueFormat[32] = {0};
428 429 430 431 432 433 434
#ifdef CUS_PROMPT
  sprintf(shell.info.promptHeader, "%s> ", CUS_PROMPT);
  sprintf(promptContinueFormat, "%%%zus> ", strlen(CUS_PROMPT));
#else
  sprintf(shell.info.promptHeader, "taos> ");
  sprintf(promptContinueFormat, "%%%zus> ", strlen("taos"));
#endif
435
  sprintf(shell.info.promptContinue, promptContinueFormat, " ");
sangshuduo's avatar
sangshuduo 已提交
436
  shell.info.promptSize = strlen(shell.info.promptHeader);
437 438 439 440 441
#ifdef TD_ENTERPRISE
  snprintf(shell.info.programVersion, sizeof(shell.info.programVersion),
           "version: %s compatible_version: %s\ngitinfo: %s\ngitinfoOfInternal: %s\nbuildInfo: %s", version,
           compatible_version, gitinfo, gitinfoOfInternal, buildinfo);
#else
442 443 444
  snprintf(shell.info.programVersion, sizeof(shell.info.programVersion),
           "version: %s compatible_version: %s\ngitinfo: %s\nbuildInfo: %s", version, compatible_version, gitinfo,
           buildinfo);
445
#endif
446 447 448 449

#if defined(_TD_WINDOWS_64) || defined(_TD_WINDOWS_32)
  shell.info.osname = "Windows";
  snprintf(shell.history.file, TSDB_FILENAME_LEN, "C:/TDengine/%s", SHELL_HISTORY_FILE);
wafwerar's avatar
wafwerar 已提交
450
  if (shellParseArgsWithoutArgp(argc, argv) != 0) return -1;
451 452 453
#elif defined(_TD_DARWIN_64)
  shell.info.osname = "Darwin";
  snprintf(shell.history.file, TSDB_FILENAME_LEN, "%s/%s", getpwuid(getuid())->pw_dir, SHELL_HISTORY_FILE);
454
  if (shellParseArgsWithoutArgp(argc, argv) != 0) return -1;
455 456 457
#else
  shell.info.osname = "Linux";
  snprintf(shell.history.file, TSDB_FILENAME_LEN, "%s/%s", getenv("HOME"), SHELL_HISTORY_FILE);
458
  shellParseArgsUseArgp(argc, argv);
459
  // if (shellParseArgsWithoutArgp(argc, argv) != 0) return -1;
460 461 462
  if (shell.args.abort) {
    return -1;
  }
463
#endif
464 465 466

  return shellCheckArgs();
}