• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

taosdata / TDengine / #5034

24 Apr 2026 11:25AM UTC coverage: 73.058%. Remained the same
#5034

push

travis-ci

web-flow
merge: from main to 3.0 branch #35224

merge: from main to 3.0 branch[manual-only]

1336 of 1975 new or added lines in 48 files covered. (67.65%)

14149 existing lines in 164 files now uncovered.

275896 of 377640 relevant lines covered (73.06%)

132944440.29 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

70.2
/source/dnode/mgmt/exe/dmMain.c
1
/*
2
 * Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
3
 *
4
 * This program is free software: you can use, redistribute, and/or modify
5
 * it under the terms of the GNU Affero General Public License, version 3
6
 * or later ("AGPL"), as published by the Free Software Foundation.
7
 *
8
 * This program is distributed in the hope that it will be useful, but WITHOUT
9
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10
 * FITNESS FOR A PARTICULAR PURPOSE.
11
 *
12
 * You should have received a copy of the GNU Affero General Public License
13
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14
 */
15
#define _DEFAULT_SOURCE
16

17
#include "dmMgmt.h"
18
#include "dmUtil.h"
19
#include "mnode.h"
20
#include "osEnv.h"
21
#include "osFile.h"
22
#include "qworker.h"
23
#include "tconfig.h"
24
#include "tconv.h"
25
#include "tglobal.h"
26
#include "tss.h"
27
#include "version.h"
28

29
#ifdef TD_JEMALLOC_ENABLED
30
#define ALLOW_FORBID_FUNC
31
#include "jemalloc/jemalloc.h"
32
#endif
33

34
#include "cus_name.h"
35

36
// clang-format off
37
#define DM_APOLLO_URL    "The apollo string to use when configuring the server, such as: -a 'jsonFile:./tests/cfg.json', cfg.json text can be '{\"fqdn\":\"td1\"}'."
38
#define DM_CFG_DIR       "Configuration directory."
39
#define DM_DMP_CFG       "Dump configuration."
40
#define DM_SDB_INFO      "Dump sdb info."
41
#define DM_ENV_CMD       "The env cmd variable string to use when configuring the server, such as: -e 'TAOS_FQDN=td1'."
42
#define DM_ENV_FILE      "The env variable file path to use when configuring the server, default is './.env', .env text can be 'TAOS_FQDN=td1'."
43
#define DM_MACHINE_CODE  "Get machine code."
44
#define DM_LOG_OUTPUT    "Specify log output. Options:\n\r\t\t\t   stdout, stderr, /dev/null, <directory>, <directory>/<filename>, <filename>\n\r\t\t\t   * If OUTPUT contains an absolute directory, logs will be stored in that directory instead of logDir.\n\r\t\t\t   * If OUTPUT contains a relative directory, logs will be stored in the directory combined with logDir and the relative directory."
45
#define DM_SOD_ENFORCE   "\t   Enable mandatory Separation of Duties (SoD). This parameter only applies to mnode leader. Once SYSDBA, SYSSEC, and SYSAUDIT\n\r\t\t\t   roles are assigned to separate regular users, the root account will be disabled permanently."
46
#define DM_VERSION       "Print program version."
47
#define DM_EMAIL         "<support@taosdata.com>"
48
#define DM_MEM_DBG       "Enable memory debug"
49
#define DM_SET_ENCRYPTKEY  "Set encrypt key. such as: -y 1234567890abcdef, the length should be less or equal to 16."
50
#define DM_REPAIR_MODE   "Start repair mode."
51

52
typedef enum {
53
  DM_REPAIR_TARGET_META = 0,
54
  DM_REPAIR_TARGET_TSDB,
55
  DM_REPAIR_TARGET_WAL,
56
} EDmRepairTargetType;
57

58
typedef struct {
59
  uint8_t reserved;
60
} SRepairWalVnodeOpt;
61

62
typedef struct {
63
  SHashObj *pByVnode;  // key: int32_t vnodeId, value: SRepairMetaVnodeOpt
64
  int32_t   numOfVnodes;
65
  bool      enabled;
66
} SRepairMetaOpt;
67

68
typedef struct {
69
  SHashObj *pByFileId;  // key: int32_t fileId, value: SRepairTsdbFileOpt
70
  int32_t   numOfFiles;
71
  bool      allFiles;
72
  SRepairTsdbFileOpt allFileOpt;
73
} SRepairTsdbVnodeOpt;
74

75
typedef struct {
76
  SHashObj *pByVnode;  // key: int32_t vnodeId, value: SRepairTsdbVnodeOpt
77
  int32_t   numOfVnodes;
78
  bool      enabled;
79
} SRepairTsdbOpt;
80

81
typedef struct {
82
  SHashObj *pByVnode;  // key: int32_t vnodeId, value: SRepairWalVnodeOpt
83
  int32_t   numOfVnodes;
84
  bool      enabled;
85
} SRepairWalOpt;
86

87
typedef struct {
88
  EDmRepairTargetType type;
89
  int32_t             vnodeId;
90
  int32_t             fileId;
91
  bool                fileIdIsWildcard;
92
  EDmRepairStrategy   strategy;
93
} SDmParsedRepairTarget;
94

95
typedef struct {
96
  SRepairWalOpt walOpt;
97
  SRepairMetaOpt metaOpt;
98
  SRepairTsdbOpt tsdbOpt;
99
} SRepairVnodeOpt;
100

101
typedef struct {
102
  bool withR;                 // -r
103
  bool hasRepairArgs;
104
  bool hasNodeType;           // --node-type
105
  bool hasBackupPath;         // --backup-path
106
  bool hasMode;               // --mode
107
  char nodeType[32];          // --node-type: vnode(only supported option now)|mnode|snode
108
  char backupPath[PATH_MAX];  // --backup-path
109
  char mode[32];              // --mode
110
                              //   force: single node recovery mode. (Recovery as mush data as possible with local info)
111
                              //   copy: copy from backup
112
                              //   replica: form replica
113
  SRepairVnodeOpt vnodeOpt;
114
  // SRepairMnodeOpt mnodeOpt;
115
  // SRepairSnodeOpt snodeOpt;
116
} SDmRepairOption;
117
// clang-format on
118

119
static struct {
120
#ifdef WINDOWS
121
  bool winServiceMode;
122
#endif
123
  bool dumpConfig;
124
  bool dumpSdb;
125
  bool deleteTrans;
126
  bool modifySdb;
127
  char sdbJsonFile[PATH_MAX];
128
  bool generateGrant;
129
  bool memDbg;
130

131
#ifdef USE_SHARED_STORAGE
132
  bool checkSs;
133
#endif
134

135
  bool            printAuth;
136
  bool            printVersion;
137
  bool            printHelp;
138
  bool            printRepairHelp;
139
  char            envFile[PATH_MAX];
140
  char            apolloUrl[PATH_MAX];
141
  const char    **envCmd;
142
  SArray         *pArgs;  // SConfigPair
143
  int64_t         startTime;
144
  bool            generateCode;
145
  bool            runRepairFlow;
146
  char            encryptKey[ENCRYPT_KEY_LEN + 1];
147
  SDmRepairOption repairOpt;
148
} global = {0};
149

150
extern int32_t cryptLoadProviders();
151
static int32_t dmFinalizeRepairOption(void);
152
static int32_t dmParseArgs(int32_t argc, char const *argv[]);
UNCOV
153
static void    dmSetDebugFlag(int32_t signum, void *sigInfo, void *context) { (void)taosSetGlobalDebugFlag(143); }
×
154
static void    dmSetAssert(int32_t signum, void *sigInfo, void *context) { tsAssert = 1; }
×
155

156
static void dmStopDnode(int signum, void *sigInfo, void *context) {
677,429✔
157
  // taosIgnSignal(SIGUSR1);
158
  // taosIgnSignal(SIGUSR2);
159
#ifndef TD_ASTRA
160
  if (taosIgnSignal(SIGTERM) != 0) {
677,429✔
UNCOV
161
    dWarn("failed to ignore signal SIGTERM");
×
162
  }
163
  if (taosIgnSignal(SIGHUP) != 0) {
677,608✔
UNCOV
164
    dWarn("failed to ignore signal SIGHUP");
×
165
  }
166
  if (taosIgnSignal(SIGINT) != 0) {
677,608✔
UNCOV
167
    dWarn("failed to ignore signal SIGINT");
×
168
  }
169
  if (taosIgnSignal(SIGABRT) != 0) {
677,608✔
UNCOV
170
    dWarn("failed to ignore signal SIGABRT");
×
171
  }
172
  if (taosIgnSignal(SIGBREAK) != 0) {
677,608✔
173
    dWarn("failed to ignore signal SIGBREAK");
677,608✔
174
  }
175
#endif
176
  dInfo("shut down signal is %d", signum);
677,608✔
177
#if !defined(WINDOWS) && !defined(TD_ASTRA)
178
  if (sigInfo != NULL) {
677,608✔
179
    dInfo("sender PID:%d cmdline:%s", ((siginfo_t *)sigInfo)->si_pid,
677,608✔
180
          taosGetCmdlineByPID(((siginfo_t *)sigInfo)->si_pid));
181
  }
182
#endif
183

184
  dmStop();
677,608✔
185
}
677,608✔
186

UNCOV
187
void dmStopDaemon() { dmStopDnode(SIGTERM, NULL, NULL); }
×
188

UNCOV
189
void dmLogCrash(int signum, void *sigInfo, void *context) {
×
190
  // taosIgnSignal(SIGTERM);
191
  // taosIgnSignal(SIGHUP);
192
  // taosIgnSignal(SIGINT);
193
  // taosIgnSignal(SIGBREAK);
UNCOV
194
  dInfo("crash signal is %d", signum);
×
195

196
#ifndef WINDOWS
UNCOV
197
  if (taosIgnSignal(SIGBUS) != 0) {
×
198
    dWarn("failed to ignore signal SIGBUS");
×
199
  }
200
#endif
UNCOV
201
  if (taosIgnSignal(SIGABRT) != 0) {
×
202
    dWarn("failed to ignore signal SIGABRT");
×
203
  }
UNCOV
204
  if (taosIgnSignal(SIGFPE) != 0) {
×
205
    dWarn("failed to ignore signal SIGFPE");
×
206
  }
UNCOV
207
  if (taosIgnSignal(SIGSEGV) != 0) {
×
208
    dWarn("failed to ignore signal SIGSEGV");
×
209
  }
210
#ifdef USE_REPORT
UNCOV
211
  writeCrashLogToFile(signum, sigInfo, CUS_PROMPT "d", dmGetClusterId(), global.startTime);
×
212
#endif
213
#ifdef _TD_DARWIN_64
214
  exit(signum);
215
#elif defined(WINDOWS)
216
  // On Windows, restore default signal handler and re-raise to trigger SEH/FlCrashDump
217
  // This allows the UnhandledExceptionFilter to generate a proper minidump
218
  signal(signum, SIG_DFL);
219
  raise(signum);
220
  // If raise() returns (shouldn't happen), fall through to exit
221
  exit(signum);
222
#endif
UNCOV
223
}
×
224

225
static void dmSetSignalHandle() {
648,897✔
226
  if (taosSetSignal(SIGUSR1, dmSetDebugFlag) != 0) {
648,897✔
UNCOV
227
    dWarn("failed to set signal SIGUSR1");
×
228
  }
229
  if (taosSetSignal(SIGUSR2, dmSetAssert) != 0) {
648,897✔
UNCOV
230
    dWarn("failed to set signal SIGUSR1");
×
231
  }
232
  if (taosSetSignal(SIGTERM, dmStopDnode) != 0) {
648,897✔
UNCOV
233
    dWarn("failed to set signal SIGUSR1");
×
234
  }
235
  if (taosSetSignal(SIGHUP, dmStopDnode) != 0) {
648,897✔
UNCOV
236
    dWarn("failed to set signal SIGUSR1");
×
237
  }
238
  if (taosSetSignal(SIGINT, dmStopDnode) != 0) {
648,897✔
UNCOV
239
    dWarn("failed to set signal SIGUSR1");
×
240
  }
241
  if (taosSetSignal(SIGBREAK, dmStopDnode) != 0) {
648,897✔
242
    dWarn("failed to set signal SIGUSR1");
648,897✔
243
  }
244
  if (taosSetSignal(SIGABRT, dmLogCrash) != 0) {
648,897✔
UNCOV
245
    dWarn("failed to set signal SIGUSR1");
×
246
  }
247
  if (taosSetSignal(SIGFPE, dmLogCrash) != 0) {
648,897✔
UNCOV
248
    dWarn("failed to set signal SIGUSR1");
×
249
  }
250
  if (taosSetSignal(SIGSEGV, dmLogCrash) != 0) {
648,897✔
UNCOV
251
    dWarn("failed to set signal SIGUSR1");
×
252
  }
253
#ifndef WINDOWS
254
  if (taosSetSignal(SIGTSTP, dmStopDnode) != 0) {
648,897✔
UNCOV
255
    dWarn("failed to set signal SIGUSR1");
×
256
  }
257
  if (taosSetSignal(SIGQUIT, dmStopDnode) != 0) {
648,897✔
UNCOV
258
    dWarn("failed to set signal SIGUSR1");
×
259
  }
260
  if (taosSetSignal(SIGBUS, dmLogCrash) != 0) {
648,897✔
UNCOV
261
    dWarn("failed to set signal SIGUSR1");
×
262
  }
263
#endif
264
}
648,897✔
265

266
static bool dmMatchLongOption(const char *arg, const char *opt, const char **pVal) {
2,667,970✔
267
  int32_t optLen = (int32_t)strlen(opt);
2,667,970✔
268
  if (strncmp(arg, opt, optLen) != 0) {
2,667,970✔
269
    return false;
2,662,810✔
270
  }
271

272
  if (arg[optLen] == '\0') {
5,160✔
273
    *pVal = NULL;
5,160✔
274
    return true;
5,160✔
275
  }
276

UNCOV
277
  if (arg[optLen] == '=') {
×
278
    *pVal = arg + optLen + 1;
×
279
    return true;
×
280
  }
281

UNCOV
282
  return false;
×
283
}
284

285
static int32_t dmParseLongOptionValue(int32_t argc, char const *argv[], int32_t *pIndex, const char *opt, char *buf,
2,667,970✔
286
                                      int32_t bufLen, bool *pMatched) {
287
  const char *val = NULL;
2,667,970✔
288
  *pMatched = false;
2,667,970✔
289
  if (!dmMatchLongOption(argv[*pIndex], opt, &val)) {
2,667,970✔
290
    return TSDB_CODE_SUCCESS;
2,662,810✔
291
  }
292
  *pMatched = true;
5,160✔
293

294
  if (val == NULL) {
5,160✔
295
    if (*pIndex >= argc - 1 || argv[*pIndex + 1][0] == '-') {
5,160✔
UNCOV
296
      printf("'%s' requires a parameter\n", opt);
×
297
      return TSDB_CODE_INVALID_PARA;
×
298
    }
299
    val = argv[++(*pIndex)];
5,160✔
300
  }
301

302
  int32_t vLen = (int32_t)strlen(val);
5,160✔
303
  if (vLen <= 0 || vLen >= bufLen) {
5,160✔
UNCOV
304
    printf("invalid value for '%s'\n", opt);
×
305
    return TSDB_CODE_INVALID_PARA;
×
306
  }
307

308
  tstrncpy(buf, val, bufLen);
5,160✔
309
  return TSDB_CODE_SUCCESS;
5,160✔
310
}
311

312
static const char *dmRepairFileTypeName(EDmRepairTargetType fileType) {
32✔
313
  switch (fileType) {
32✔
314
    case DM_REPAIR_TARGET_META:
16✔
315
      return "meta";
16✔
316
    case DM_REPAIR_TARGET_TSDB:
16✔
317
      return "tsdb";
16✔
UNCOV
318
    case DM_REPAIR_TARGET_WAL:
×
319
      return "wal";
×
320
    default:
×
321
      return "unknown";
×
322
  }
323
}
324

325
static int32_t dmRepairTargetError(const char *raw, const char *reason) {
96✔
326
  printf("invalid '--repair-target %s': %s\n", raw, reason);
96✔
327
  return TSDB_CODE_INVALID_PARA;
96✔
328
}
329

330
static void dmCleanupMetaRepairOpt(SRepairMetaOpt *pOpt) {
661,138✔
331
  if (pOpt->pByVnode != NULL) {
661,138✔
332
    taosHashCleanup(pOpt->pByVnode);
475✔
333
    pOpt->pByVnode = NULL;
475✔
334
  }
335
  pOpt->numOfVnodes = 0;
661,138✔
336
  pOpt->enabled = false;
661,138✔
337
}
661,138✔
338

339
static void dmCleanupWalRepairOpt(SRepairWalOpt *pOpt) {
661,138✔
340
  if (pOpt->pByVnode != NULL) {
661,138✔
341
    taosHashCleanup(pOpt->pByVnode);
145✔
342
    pOpt->pByVnode = NULL;
145✔
343
  }
344
  pOpt->numOfVnodes = 0;
661,138✔
345
  pOpt->enabled = false;
661,138✔
346
}
661,138✔
347

348
static void dmCleanupTsdbRepairOpt(SRepairTsdbOpt *pOpt) {
661,138✔
349
  if (pOpt->pByVnode != NULL) {
661,138✔
350
    SRepairTsdbVnodeOpt *pVnodeOpt = taosHashIterate(pOpt->pByVnode, NULL);
778✔
351
    while (pVnodeOpt != NULL) {
1,556✔
352
      if (pVnodeOpt->pByFileId != NULL) {
778✔
353
        taosHashCleanup(pVnodeOpt->pByFileId);
778✔
354
        pVnodeOpt->pByFileId = NULL;
778✔
355
      }
356
      pVnodeOpt = taosHashIterate(pOpt->pByVnode, pVnodeOpt);
778✔
357
    }
358

359
    taosHashCleanup(pOpt->pByVnode);
778✔
360
    pOpt->pByVnode = NULL;
778✔
361
  }
362

363
  pOpt->numOfVnodes = 0;
661,138✔
364
  pOpt->enabled = false;
661,138✔
365
}
661,138✔
366

367
static void dmCleanupRepairOption(SDmRepairOption *pOpt) {
661,138✔
368
  dmCleanupMetaRepairOpt(&pOpt->vnodeOpt.metaOpt);
661,138✔
369
  dmCleanupTsdbRepairOpt(&pOpt->vnodeOpt.tsdbOpt);
661,138✔
370
  dmCleanupWalRepairOpt(&pOpt->vnodeOpt.walOpt);
661,138✔
371
}
661,138✔
372

373
static bool dmParseRepairFileType(const char *token, EDmRepairTargetType *pFileType) {
1,601✔
374
  if (strcmp(token, "meta") == 0) {
1,601✔
375
    *pFileType = DM_REPAIR_TARGET_META;
523✔
376
    return true;
523✔
377
  }
378
  if (strcmp(token, "tsdb") == 0) {
1,078✔
379
    *pFileType = DM_REPAIR_TARGET_TSDB;
901✔
380
    return true;
901✔
381
  }
382
  if (strcmp(token, "wal") == 0) {
177✔
383
    *pFileType = DM_REPAIR_TARGET_WAL;
161✔
384
    return true;
161✔
385
  }
386

387
  return false;
16✔
388
}
389

390
static bool dmParseRepairStrategy(EDmRepairTargetType fileType, const char *value, EDmRepairStrategy *pStrategy) {
287✔
391
  if (fileType == DM_REPAIR_TARGET_META) {
287✔
392
    if (strcmp(value, "from_uid") == 0) {
118✔
UNCOV
393
      *pStrategy = DM_REPAIR_STRATEGY_META_FROM_UID;
×
394
      return true;
×
395
    }
396
    if (strcmp(value, "from_redo") == 0) {
118✔
397
      *pStrategy = DM_REPAIR_STRATEGY_META_FROM_REDO;
102✔
398
      return true;
102✔
399
    }
400
    return false;
16✔
401
  }
402

403
  if (fileType == DM_REPAIR_TARGET_TSDB) {
169✔
404
    if (strcmp(value, "drop_invalid_only") == 0) {
169✔
UNCOV
405
      *pStrategy = DM_REPAIR_STRATEGY_TSDB_DROP_INVALID_ONLY;
×
406
      return true;
×
407
    }
408
    if (strcmp(value, "head_only_rebuild") == 0) {
169✔
409
      *pStrategy = DM_REPAIR_STRATEGY_TSDB_HEAD_ONLY_REBUILD;
81✔
410
      return true;
81✔
411
    }
412
    if (strcmp(value, "full_rebuild") == 0) {
88✔
413
      *pStrategy = DM_REPAIR_STRATEGY_TSDB_FULL_REBUILD;
72✔
414
      return true;
72✔
415
    }
416
    return false;
16✔
417
  }
418

UNCOV
419
  return false;
×
420
}
421

422
static EDmRepairStrategy dmDefaultRepairStrategy(EDmRepairTargetType fileType) {
1,250✔
423
  switch (fileType) {
1,250✔
424
    case DM_REPAIR_TARGET_META:
389✔
425
      return DM_REPAIR_STRATEGY_META_FROM_UID;
389✔
426
    case DM_REPAIR_TARGET_TSDB:
716✔
427
      return DM_REPAIR_STRATEGY_TSDB_DROP_INVALID_ONLY;
716✔
428
    default:
145✔
429
      return DM_REPAIR_STRATEGY_NONE;
145✔
430
  }
431
}
432

433
static int32_t dmParseRepairPositiveInt(const char *rawTarget, const char *key, const char *value, int32_t *pOut) {
2,380✔
434
  char   *end = NULL;
2,380✔
435
  int32_t parsed = taosStr2Int32(value, &end, 10);
2,380✔
436
  if (value[0] == '\0' || end == NULL || *end != '\0' || parsed <= 0) {
2,380✔
UNCOV
437
    char reason[128] = {0};
×
438
    snprintf(reason, sizeof(reason), "invalid value '%s' for key '%s'", value, key);
×
439
    return dmRepairTargetError(rawTarget, reason);
×
440
  }
441

442
  *pOut = parsed;
2,380✔
443
  return TSDB_CODE_SUCCESS;
2,380✔
444
}
445

446
static int32_t dmEnsureMetaRepairHash(SRepairMetaOpt *pOpt) {
491✔
447
  if (pOpt->pByVnode != NULL) {
491✔
448
    return TSDB_CODE_SUCCESS;
16✔
449
  }
450

451
  pOpt->pByVnode = taosHashInit(4, taosGetDefaultHashFunction(TSDB_DATA_TYPE_INT), false, HASH_NO_LOCK);
475✔
452
  return pOpt->pByVnode == NULL ? terrno : TSDB_CODE_SUCCESS;
475✔
453
}
454

455
static int32_t dmEnsureWalRepairHash(SRepairWalOpt *pOpt) {
145✔
456
  if (pOpt->pByVnode != NULL) {
145✔
UNCOV
457
    return TSDB_CODE_SUCCESS;
×
458
  }
459

460
  pOpt->pByVnode = taosHashInit(4, taosGetDefaultHashFunction(TSDB_DATA_TYPE_INT), false, HASH_NO_LOCK);
145✔
461
  return pOpt->pByVnode == NULL ? terrno : TSDB_CODE_SUCCESS;
145✔
462
}
463

464
static int32_t dmEnsureTsdbRepairHash(SRepairTsdbOpt *pOpt) {
869✔
465
  if (pOpt->pByVnode != NULL) {
869✔
466
    return TSDB_CODE_SUCCESS;
91✔
467
  }
468

469
  pOpt->pByVnode = taosHashInit(4, taosGetDefaultHashFunction(TSDB_DATA_TYPE_INT), false, HASH_NO_LOCK);
778✔
470
  return pOpt->pByVnode == NULL ? terrno : TSDB_CODE_SUCCESS;
778✔
471
}
472

473
static int32_t dmInsertMetaRepairTarget(SDmRepairOption *pOpt, int32_t vnodeId, EDmRepairStrategy strategy) {
491✔
474
  int32_t code = dmEnsureMetaRepairHash(&pOpt->vnodeOpt.metaOpt);
491✔
475
  if (code != TSDB_CODE_SUCCESS) {
491✔
UNCOV
476
    return code;
×
477
  }
478

479
  if (taosHashGet(pOpt->vnodeOpt.metaOpt.pByVnode, &vnodeId, sizeof(vnodeId)) != NULL) {
491✔
480
    printf("duplicated repair target for meta vnode %d\n", vnodeId);
16✔
481
    return TSDB_CODE_INVALID_PARA;
16✔
482
  }
483

484
  SRepairMetaVnodeOpt vnodeOpt = {.strategy = strategy};
475✔
485
  code = taosHashPut(pOpt->vnodeOpt.metaOpt.pByVnode, &vnodeId, sizeof(vnodeId), &vnodeOpt, sizeof(vnodeOpt));
475✔
486
  if (code != TSDB_CODE_SUCCESS) {
475✔
UNCOV
487
    return code;
×
488
  }
489

490
  pOpt->vnodeOpt.metaOpt.enabled = true;
475✔
491
  pOpt->vnodeOpt.metaOpt.numOfVnodes++;
475✔
492
  return TSDB_CODE_SUCCESS;
475✔
493
}
494

495
static int32_t dmInsertWalRepairTarget(SDmRepairOption *pOpt, int32_t vnodeId) {
145✔
496
  int32_t code = dmEnsureWalRepairHash(&pOpt->vnodeOpt.walOpt);
145✔
497
  if (code != TSDB_CODE_SUCCESS) {
145✔
UNCOV
498
    return code;
×
499
  }
500

501
  if (taosHashGet(pOpt->vnodeOpt.walOpt.pByVnode, &vnodeId, sizeof(vnodeId)) != NULL) {
145✔
UNCOV
502
    printf("duplicated repair target for wal vnode %d\n", vnodeId);
×
503
    return TSDB_CODE_INVALID_PARA;
×
504
  }
505

506
  SRepairWalVnodeOpt vnodeOpt = {.reserved = 0};
145✔
507
  code = taosHashPut(pOpt->vnodeOpt.walOpt.pByVnode, &vnodeId, sizeof(vnodeId), &vnodeOpt, sizeof(vnodeOpt));
145✔
508
  if (code != TSDB_CODE_SUCCESS) {
145✔
UNCOV
509
    return code;
×
510
  }
511

512
  pOpt->vnodeOpt.walOpt.enabled = true;
145✔
513
  pOpt->vnodeOpt.walOpt.numOfVnodes++;
145✔
514
  return TSDB_CODE_SUCCESS;
145✔
515
}
516

517
static int32_t dmInsertTsdbRepairTarget(SDmRepairOption *pOpt, int32_t vnodeId, int32_t fileId,
869✔
518
                                        bool fileIdIsWildcard, EDmRepairStrategy strategy) {
519
  int32_t code = dmEnsureTsdbRepairHash(&pOpt->vnodeOpt.tsdbOpt);
869✔
520
  if (code != TSDB_CODE_SUCCESS) {
869✔
UNCOV
521
    return code;
×
522
  }
523

524
  SRepairTsdbVnodeOpt *pVnodeOpt = taosHashGet(pOpt->vnodeOpt.tsdbOpt.pByVnode, &vnodeId, sizeof(vnodeId));
869✔
525
  if (pVnodeOpt == NULL) {
869✔
526
    SRepairTsdbVnodeOpt vnodeOpt = {0};
778✔
527
    vnodeOpt.pByFileId = taosHashInit(4, taosGetDefaultHashFunction(TSDB_DATA_TYPE_INT), false, HASH_NO_LOCK);
778✔
528
    if (vnodeOpt.pByFileId == NULL) {
778✔
UNCOV
529
      return terrno;
×
530
    }
531

532
    code = taosHashPut(pOpt->vnodeOpt.tsdbOpt.pByVnode, &vnodeId, sizeof(vnodeId), &vnodeOpt, sizeof(vnodeOpt));
778✔
533
    if (code != TSDB_CODE_SUCCESS) {
778✔
UNCOV
534
      taosHashCleanup(vnodeOpt.pByFileId);
×
535
      return code;
×
536
    }
537

538
    pOpt->vnodeOpt.tsdbOpt.numOfVnodes++;
778✔
539
    pVnodeOpt = taosHashGet(pOpt->vnodeOpt.tsdbOpt.pByVnode, &vnodeId, sizeof(vnodeId));
778✔
540
    if (pVnodeOpt == NULL) {
778✔
UNCOV
541
      return TSDB_CODE_FAILED;
×
542
    }
543
  }
544

545
  if (fileIdIsWildcard) {
869✔
546
    if (pVnodeOpt->allFiles || pVnodeOpt->numOfFiles > 0) {
90✔
547
      printf("fileid=* overlaps existing tsdb repair targets for vnode %d\n", vnodeId);
16✔
548
      return TSDB_CODE_INVALID_PARA;
16✔
549
    }
550

551
    pVnodeOpt->allFiles = true;
74✔
552
    pVnodeOpt->allFileOpt.strategy = strategy;
74✔
553
    pOpt->vnodeOpt.tsdbOpt.enabled = true;
74✔
554
    return TSDB_CODE_SUCCESS;
74✔
555
  }
556

557
  if (pVnodeOpt->allFiles) {
779✔
558
    printf("fileid=* overlaps existing tsdb repair targets for vnode %d\n", vnodeId);
16✔
559
    return TSDB_CODE_INVALID_PARA;
16✔
560
  }
561

562
  if (taosHashGet(pVnodeOpt->pByFileId, &fileId, sizeof(fileId)) != NULL) {
763✔
563
    printf("duplicated repair target for tsdb vnode %d fileid %d\n", vnodeId, fileId);
16✔
564
    return TSDB_CODE_INVALID_PARA;
16✔
565
  }
566

567
  SRepairTsdbFileOpt fileOpt = {.strategy = strategy};
747✔
568
  code = taosHashPut(pVnodeOpt->pByFileId, &fileId, sizeof(fileId), &fileOpt, sizeof(fileOpt));
747✔
569
  if (code != TSDB_CODE_SUCCESS) {
747✔
UNCOV
570
    return code;
×
571
  }
572

573
  pVnodeOpt->numOfFiles++;
747✔
574
  pOpt->vnodeOpt.tsdbOpt.enabled = true;
747✔
575
  return TSDB_CODE_SUCCESS;
747✔
576
}
577

578
static int32_t dmParseRepairTarget(const char *raw, SDmParsedRepairTarget *pTarget) {
1,601✔
579
  char buf[PATH_MAX] = {0};
1,601✔
580
  tstrncpy(buf, raw, sizeof(buf));
1,601✔
581

582
  memset(pTarget, 0, sizeof(*pTarget));
1,601✔
583

584
  bool  hasFileType = false;
1,601✔
585
  bool  hasVnode = false;
1,601✔
586
  bool  hasFileId = false;
1,601✔
587
  bool  hasStrategy = false;
1,601✔
588
  char *cursor = buf;
1,601✔
589

590
  while (cursor != NULL) {
4,390✔
591
    char *next = strchr(cursor, ':');
4,390✔
592
    if (next != NULL) {
4,390✔
593
      *next = '\0';
2,805✔
594
    }
595

596
    if (cursor[0] == '\0') {
4,390✔
UNCOV
597
      return dmRepairTargetError(raw, "empty segment is not allowed");
×
598
    }
599

600
    if (!hasFileType) {
4,390✔
601
      if (!dmParseRepairFileType(cursor, &pTarget->type)) {
1,601✔
602
        char reason[128] = {0};
16✔
603
        snprintf(reason, sizeof(reason), "unknown file type '%s'", cursor);
16✔
604
        return dmRepairTargetError(raw, reason);
16✔
605
      }
606
      hasFileType = true;
1,585✔
607
    } else {
608
      char *eq = strchr(cursor, '=');
2,789✔
609
      if (eq == NULL || eq == cursor || eq[1] == '\0') {
2,789✔
UNCOV
610
        return dmRepairTargetError(raw, "expected key=value after ':'");
×
611
      }
612

613
      *eq = '\0';
2,789✔
614
      const char *key = cursor;
2,789✔
615
      const char *value = eq + 1;
2,789✔
616

617
      if (strcmp(key, "vnode") == 0) {
2,789✔
618
        if (hasVnode) {
1,601✔
619
          return dmRepairTargetError(raw, "duplicated key 'vnode'");
16✔
620
        }
621
        int32_t code = dmParseRepairPositiveInt(raw, key, value, &pTarget->vnodeId);
1,585✔
622
        if (code != TSDB_CODE_SUCCESS) {
1,585✔
UNCOV
623
          return code;
×
624
        }
625
        hasVnode = true;
1,585✔
626
      } else if (strcmp(key, "fileid") == 0) {
1,188✔
627
        if (pTarget->type != DM_REPAIR_TARGET_TSDB) {
885✔
UNCOV
628
          char reason[128] = {0};
×
629
          snprintf(reason, sizeof(reason), "key 'fileid' is not allowed for file type '%s'",
×
630
                   dmRepairFileTypeName(pTarget->type));
UNCOV
631
          return dmRepairTargetError(raw, reason);
×
632
        }
633
        if (hasFileId) {
885✔
UNCOV
634
          return dmRepairTargetError(raw, "duplicated key 'fileid'");
×
635
        }
636
        if (strcmp(value, "*") == 0) {
885✔
637
          pTarget->fileId = 0;
90✔
638
          pTarget->fileIdIsWildcard = true;
90✔
639
        } else {
640
          int32_t code = dmParseRepairPositiveInt(raw, key, value, &pTarget->fileId);
795✔
641
          if (code != TSDB_CODE_SUCCESS) {
795✔
UNCOV
642
            return code;
×
643
          }
644
        }
645
        hasFileId = true;
885✔
646
      } else if (strcmp(key, "strategy") == 0) {
303✔
647
        if (pTarget->type == DM_REPAIR_TARGET_WAL) {
303✔
648
          return dmRepairTargetError(raw, "key 'strategy' is not supported for file type 'wal' in current phase");
16✔
649
        }
650
        if (hasStrategy) {
287✔
UNCOV
651
          return dmRepairTargetError(raw, "duplicated key 'strategy'");
×
652
        }
653
        if (!dmParseRepairStrategy(pTarget->type, value, &pTarget->strategy)) {
287✔
654
          char reason[160] = {0};
32✔
655
          snprintf(reason, sizeof(reason), "invalid strategy '%s' for file type '%s'", value,
32✔
656
                   dmRepairFileTypeName(pTarget->type));
657
          return dmRepairTargetError(raw, reason);
32✔
658
        }
659
        hasStrategy = true;
255✔
660
      } else {
UNCOV
661
        char reason[128] = {0};
×
662
        snprintf(reason, sizeof(reason), "unknown key '%s'", key);
×
663
        return dmRepairTargetError(raw, reason);
×
664
      }
665
    }
666

667
    if (next == NULL) {
4,310✔
668
      break;
1,521✔
669
    }
670
    cursor = next + 1;
2,789✔
671
  }
672

673
  if (!hasVnode) {
1,521✔
UNCOV
674
    return dmRepairTargetError(raw, "missing required key 'vnode'");
×
675
  }
676

677
  if (pTarget->type == DM_REPAIR_TARGET_TSDB && !hasFileId) {
1,521✔
678
    return dmRepairTargetError(raw, "missing required key 'fileid'");
16✔
679
  }
680

681
  if (!hasStrategy) {
1,505✔
682
    pTarget->strategy = dmDefaultRepairStrategy(pTarget->type);
1,250✔
683
  }
684

685
  return TSDB_CODE_SUCCESS;
1,505✔
686
}
687

688
static int32_t dmValidateRepairOption() {
1,318✔
689
  SDmRepairOption *pOpt = &global.repairOpt;
1,318✔
690

691
  if (!pOpt->hasMode) {
1,318✔
692
    printf("missing '--mode' in repair mode\n");
16✔
693
    return TSDB_CODE_INVALID_PARA;
16✔
694
  }
695
  if (strcmp(pOpt->mode, "force") != 0) {
1,302✔
UNCOV
696
    printf("'--repair-target' requires '--mode force'\n");
×
697
    return TSDB_CODE_INVALID_PARA;
×
698
  }
699

700
  if (!pOpt->hasNodeType) {
1,302✔
701
    printf("missing '--node-type' in repair mode\n");
16✔
702
    return TSDB_CODE_INVALID_PARA;
16✔
703
  }
704
  if (strcmp(pOpt->nodeType, "vnode") != 0) {
1,286✔
705
    printf("'--repair-target' currently only supports '--node-type vnode'\n");
16✔
706
    return TSDB_CODE_OPS_NOT_SUPPORT;
16✔
707
  }
708

709
  if (!pOpt->vnodeOpt.metaOpt.enabled && !pOpt->vnodeOpt.tsdbOpt.enabled && !pOpt->vnodeOpt.walOpt.enabled) {
1,270✔
710
    printf("missing '--repair-target' in repair mode\n");
16✔
711
    return TSDB_CODE_INVALID_PARA;
16✔
712
  }
713

714
  return TSDB_CODE_SUCCESS;
1,254✔
715
}
716

717
static int32_t dmParseRepairOption(int32_t argc, char const *argv[], int32_t *pIndex, bool *pParsed) {
668,768✔
718
  int32_t          code = TSDB_CODE_SUCCESS;
668,768✔
719
  int32_t          index = *pIndex;
668,768✔
720
  bool             matched = false;
668,768✔
721
  bool             optMatched = false;
668,768✔
722
  SDmRepairOption *pOpt = &global.repairOpt;
668,768✔
723

724
  *pParsed = false;
668,768✔
725

726
  code = dmParseLongOptionValue(argc, argv, &index, "--node-type", pOpt->nodeType, sizeof(pOpt->nodeType), &optMatched);
668,768✔
727
  if (code != 0) return code;
668,768✔
728
  if (optMatched) {
668,768✔
729
    pOpt->hasRepairArgs = true;
1,494✔
730
    pOpt->hasNodeType = true;
1,494✔
731
    matched = true;
1,494✔
732
  }
733

734
  if (!matched) {
668,768✔
735
    code = dmParseLongOptionValue(argc, argv, &index, "--backup-path", pOpt->backupPath, sizeof(pOpt->backupPath),
667,274✔
736
                                  &optMatched);
737
    if (code != 0) return code;
667,274✔
738
    if (optMatched) {
667,274✔
739
      pOpt->hasRepairArgs = true;
555✔
740
      pOpt->hasBackupPath = true;
555✔
741
      matched = true;
555✔
742
    }
743
  }
744

745
  if (!matched) {
668,768✔
746
    code = dmParseLongOptionValue(argc, argv, &index, "--mode", pOpt->mode, sizeof(pOpt->mode), &optMatched);
666,719✔
747
    if (code != 0) return code;
666,719✔
748
    if (optMatched) {
666,719✔
749
      pOpt->hasRepairArgs = true;
1,510✔
750
      pOpt->hasMode = true;
1,510✔
751
      matched = true;
1,510✔
752
    }
753
  }
754

755
  if (!matched) {
668,768✔
756
    char targetBuf[PATH_MAX] = {0};
665,209✔
757
    code = dmParseLongOptionValue(argc, argv, &index, "--repair-target", targetBuf, sizeof(targetBuf), &optMatched);
665,209✔
758
    if (code != 0) return code;
665,369✔
759
    if (optMatched) {
665,209✔
760
      SDmParsedRepairTarget target = {0};
1,601✔
761
      code = dmParseRepairTarget(targetBuf, &target);
1,601✔
762
      if (code != 0) return code;
1,665✔
763
      switch (target.type) {
1,505✔
764
        case DM_REPAIR_TARGET_META:
491✔
765
          code = dmInsertMetaRepairTarget(pOpt, target.vnodeId, target.strategy);
491✔
766
          break;
491✔
767
        case DM_REPAIR_TARGET_TSDB:
869✔
768
          code = dmInsertTsdbRepairTarget(pOpt, target.vnodeId, target.fileId, target.fileIdIsWildcard,
869✔
769
                                          target.strategy);
770
          break;
869✔
771
        case DM_REPAIR_TARGET_WAL:
145✔
772
          code = dmInsertWalRepairTarget(pOpt, target.vnodeId);
145✔
773
          break;
145✔
UNCOV
774
        default:
×
775
          code = TSDB_CODE_INVALID_PARA;
×
776
          break;
×
777
      }
778
      if (code != TSDB_CODE_SUCCESS) return code;
1,505✔
779
      pOpt->hasRepairArgs = true;
1,441✔
780
      matched = true;
1,441✔
781
    }
782
  }
783

784
  if (matched) {
668,608✔
785
    *pParsed = true;
5,000✔
786
    *pIndex = index;
5,000✔
787
  }
788

789
  return TSDB_CODE_SUCCESS;
668,608✔
790
}
791

792
static int32_t dmFinalizeRepairOption() {
661,022✔
793
  SDmRepairOption *pOpt = &global.repairOpt;
661,022✔
794
  global.runRepairFlow = false;
661,022✔
795

796
  if ((pOpt->vnodeOpt.metaOpt.enabled || pOpt->vnodeOpt.tsdbOpt.enabled || pOpt->vnodeOpt.walOpt.enabled) &&
661,022✔
797
      !pOpt->withR) {
1,302✔
798
    printf("'--repair-target' must be used with '-r'\n");
16✔
799
    return TSDB_CODE_INVALID_PARA;
16✔
800
  }
801

802
  if (pOpt->hasRepairArgs && !pOpt->withR) {
661,006✔
UNCOV
803
    printf("repair options must be used with '-r'\n");
×
804
    return TSDB_CODE_INVALID_PARA;
×
805
  }
806

807
  if (global.printHelp && pOpt->withR) {
661,006✔
808
    global.printRepairHelp = true;
16✔
809
    return TSDB_CODE_SUCCESS;
16✔
810
  }
811

812
  if (!pOpt->withR) {
660,990✔
813
    return TSDB_CODE_SUCCESS;
659,672✔
814
  }
815

816
  int32_t code = dmValidateRepairOption();
1,318✔
817
  if (code == TSDB_CODE_SUCCESS) {
1,318✔
818
    global.runRepairFlow = true;
1,254✔
819
    return TSDB_CODE_SUCCESS;
1,254✔
820
  }
821

822
  if (code == TSDB_CODE_OPS_NOT_SUPPORT) {
64✔
823
    return 1;
16✔
824
  }
825

826
  return code;
48✔
827
}
828

829
bool dmRepairFlowEnabled() { return global.runRepairFlow; }
5,662,729✔
830

831
bool dmRepairNodeTypeIsVnode() { return memcmp(global.repairOpt.nodeType, "vnode", sizeof("vnode")) == 0; }
3,064✔
832

833
bool dmRepairModeIsForce() { return memcmp(global.repairOpt.mode, "force", sizeof("force")) == 0; }
3,064✔
834

835
bool dmRepairHasBackupPath() { return global.repairOpt.hasBackupPath; }
201✔
836

837
const char *dmRepairBackupPath() { return global.repairOpt.backupPath; }
136✔
838

839
const SRepairMetaVnodeOpt *dmRepairGetMetaVnodeOpt(int32_t vnodeId) {
3,131✔
840
  if (global.repairOpt.vnodeOpt.metaOpt.pByVnode == NULL) {
3,131✔
841
    return NULL;
2,331✔
842
  }
843

844
  return taosHashGet(global.repairOpt.vnodeOpt.metaOpt.pByVnode, &vnodeId, sizeof(vnodeId));
800✔
845
}
846

847
bool dmRepairNeedTsdbRepair(int32_t vnodeId) {
3,064✔
848
  if (global.repairOpt.vnodeOpt.tsdbOpt.pByVnode == NULL) {
3,064✔
849
    return false;
798✔
850
  }
851

852
  return taosHashGet(global.repairOpt.vnodeOpt.tsdbOpt.pByVnode, &vnodeId, sizeof(vnodeId)) != NULL;
2,266✔
853
}
854

855
const SRepairTsdbFileOpt *dmRepairGetTsdbFileOpt(int32_t vnodeId, int32_t fileId) {
1,530✔
856
  if (global.repairOpt.vnodeOpt.tsdbOpt.pByVnode == NULL) {
1,530✔
UNCOV
857
    return NULL;
×
858
  }
859

860
  SRepairTsdbVnodeOpt *pVnodeOpt = taosHashGet(global.repairOpt.vnodeOpt.tsdbOpt.pByVnode, &vnodeId, sizeof(vnodeId));
1,530✔
861
  if (pVnodeOpt == NULL || pVnodeOpt->pByFileId == NULL) {
1,530✔
UNCOV
862
    return (pVnodeOpt != NULL && pVnodeOpt->allFiles) ? &pVnodeOpt->allFileOpt : NULL;
×
863
  }
864

865
  const SRepairTsdbFileOpt *pFileOpt = taosHashGet(pVnodeOpt->pByFileId, &fileId, sizeof(fileId));
1,530✔
866
  if (pFileOpt != NULL) {
1,530✔
867
    return pFileOpt;
1,302✔
868
  }
869

870
  return pVnodeOpt->allFiles ? &pVnodeOpt->allFileOpt : NULL;
228✔
871
}
872

873
bool dmRepairNeedWalRepair(int32_t vnodeId) {
65✔
874
  if (global.repairOpt.vnodeOpt.walOpt.pByVnode == NULL) {
65✔
UNCOV
875
    return false;
×
876
  }
877

878
  return taosHashGet(global.repairOpt.vnodeOpt.walOpt.pByVnode, &vnodeId, sizeof(vnodeId)) != NULL;
65✔
879
}
880

881
static int32_t dmParseArgs(int32_t argc, char const *argv[]) {
661,244✔
882
  global.startTime = taosGetTimestampMs();
661,244✔
883
  memset(&global.repairOpt, 0, sizeof(global.repairOpt));
661,244✔
884

885
  int32_t cmdEnvIndex = 0;
661,244✔
886
  if (argc < 2) return 0;
661,244✔
887

888
  global.envCmd = taosMemoryMalloc((argc - 1) * sizeof(char *));
661,244✔
889
  if (global.envCmd == NULL) {
661,244✔
UNCOV
890
    return terrno;
×
891
  }
892
  memset(global.envCmd, 0, (argc - 1) * sizeof(char *));
661,244✔
893
  for (int32_t i = 1; i < argc; ++i) {
1,329,790✔
894
    bool    parsedRepairOpt = false;
668,768✔
895
    int32_t code = dmParseRepairOption(argc, argv, &i, &parsedRepairOpt);
668,768✔
896
    if (code != 0) {
668,768✔
897
      return code;
222✔
898
    }
899
    if (parsedRepairOpt) {
668,608✔
900
      continue;
5,000✔
901
    }
902

903
    if (strcmp(argv[i], "-c") == 0) {
663,608✔
904
      if (i < argc - 1) {
660,154✔
905
        if (strlen(argv[++i]) >= PATH_MAX) {
660,140✔
UNCOV
906
          printf("config file path overflow");
×
907
          return TSDB_CODE_INVALID_CFG;
×
908
        }
909
        tstrncpy(configDir, argv[i], PATH_MAX);
660,140✔
910
      } else {
911
        printf("'-c' requires a parameter, default is %s\n", configDir);
14✔
912
        return TSDB_CODE_INVALID_CFG;
14✔
913
      }
914
    } else if (strcmp(argv[i], "-a") == 0) {
3,454✔
915
      if (i < argc - 1) {
45✔
916
        if (strlen(argv[++i]) >= PATH_MAX) {
45✔
UNCOV
917
          printf("apollo url overflow");
×
918
          return TSDB_CODE_INVALID_CFG;
×
919
        }
920
        tstrncpy(global.apolloUrl, argv[i], PATH_MAX);
45✔
921
      } else {
UNCOV
922
        printf("'-a' requires a parameter\n");
×
923
        return TSDB_CODE_INVALID_CFG;
×
924
      }
925
    } else if (strcmp(argv[i], "-s") == 0) {
3,409✔
926
      global.dumpSdb = true;
61✔
927
    } else if (strcmp(argv[i], "-dTxn") == 0) {
3,348✔
UNCOV
928
      global.deleteTrans = true;
×
929
    } else if (strcmp(argv[i], "-mSdb") == 0) {
3,348✔
UNCOV
930
      global.modifySdb = true;
×
931
      if (i < argc - 1) {
×
932
        i++;
×
933
        if (strlen(argv[i]) >= PATH_MAX) {
×
934
          printf("sdb.json file path is too long\n");
×
935
          return TSDB_CODE_INVALID_CFG;
×
936
        }
UNCOV
937
        tstrncpy(global.sdbJsonFile, argv[i], PATH_MAX);
×
938
      } else {
UNCOV
939
        printf("'-mSdb' requires sdb.json file path\n");
×
940
        return TSDB_CODE_INVALID_CFG;
×
941
      }
942
    } else if (strcmp(argv[i], "-r") == 0) {
3,348✔
943
      global.repairOpt.withR = true;
1,542✔
944
    } else if (strcmp(argv[i], "-E") == 0) {
1,806✔
945
      if (i < argc - 1) {
14✔
946
        if (strlen(argv[++i]) >= PATH_MAX) {
14✔
UNCOV
947
          printf("env file path overflow");
×
948
          return TSDB_CODE_INVALID_CFG;
×
949
        }
950
        tstrncpy(global.envFile, argv[i], PATH_MAX);
14✔
951
      } else {
UNCOV
952
        printf("'-E' requires a parameter\n");
×
953
        return TSDB_CODE_INVALID_CFG;
×
954
      }
955
    } else if (strcmp(argv[i], "-k") == 0) {
1,792✔
956
      global.generateGrant = true;
45✔
957
    } else if (taosStrncasecmp(argv[i], "--SoD=", 6) == 0) {
1,747✔
NEW
958
      if (taosStrncasecmp(argv[i], "--SoD=mandatory", 16) == 0) {
×
NEW
959
        tsSodEnforceMode = 1;
×
960
      } else {
NEW
961
        printf("'%s' has invalid value, only '--SoD=mandatory' is supported\n", argv[i]);
×
NEW
962
        return TSDB_CODE_INVALID_CFG;
×
963
      }
964
#if defined(LINUX)
965
    } else if (strcmp(argv[i], "-o") == 0 || strcmp(argv[i], "--log-output") == 0 ||
1,747✔
966
               strncmp(argv[i], "--log-output=", 13) == 0) {
898✔
967
      if ((i < argc - 1) || ((i == argc - 1) && strncmp(argv[i], "--log-output=", 13) == 0)) {
1,728✔
968
        int32_t     klen = strlen(argv[i]);
864✔
969
        int32_t     vlen = klen < 13 ? strlen(argv[++i]) : klen - 13;
864✔
970
        const char *val = argv[i];
864✔
971
        if (klen >= 13) val += 13;
864✔
972
        if (vlen <= 0 || vlen >= PATH_MAX) {
864✔
UNCOV
973
          printf("failed to set log output since invalid vlen:%d, valid range: [1, %d)\n", vlen, PATH_MAX);
×
UNCOV
974
          return TSDB_CODE_INVALID_CFG;
×
975
        }
976
        tsLogOutput = taosMemoryMalloc(PATH_MAX);
864✔
977
        if (!tsLogOutput) {
864✔
UNCOV
978
          printf("failed to set log output: '%s' since %s\n", val, tstrerror(terrno));
×
UNCOV
979
          return terrno;
×
980
        }
981
        if (taosExpandDir(val, tsLogOutput, PATH_MAX) != 0) {
864✔
982
          printf("failed to expand log output: '%s' since %s\n", val, tstrerror(terrno));
×
UNCOV
983
          return terrno;
×
984
        }
985
      } else {
986
        printf("'%s' requires a parameter\n", argv[i]);
×
987
        return TSDB_CODE_INVALID_CFG;
×
988
      }
989
#endif
990
    } else if (strcmp(argv[i], "-y") == 0) {
883✔
991
      global.generateCode = true;
×
UNCOV
992
      if (i < argc - 1) {
×
UNCOV
993
        int32_t len = strlen(argv[++i]);
×
994
        if (len < ENCRYPT_KEY_LEN_MIN) {
×
995
          printf("ERROR: Encrypt key should be at least %d characters\n", ENCRYPT_KEY_LEN_MIN);
×
UNCOV
996
          return TSDB_CODE_INVALID_CFG;
×
997
        }
UNCOV
998
        if (len > ENCRYPT_KEY_LEN) {
×
999
          printf("ERROR: Encrypt key overflow, it should be at most %d characters\n", ENCRYPT_KEY_LEN);
×
1000
          return TSDB_CODE_INVALID_CFG;
×
1001
        }
1002
        tstrncpy(global.encryptKey, argv[i], ENCRYPT_KEY_LEN + 1);
×
1003
      } else {
1004
        printf("'-y' requires a parameter\n");
×
UNCOV
1005
        return TSDB_CODE_INVALID_CFG;
×
1006
      }
1007
    } else if (strcmp(argv[i], "-C") == 0) {
883✔
1008
      global.dumpConfig = true;
45✔
1009
    } else if (strcmp(argv[i], "-V") == 0 || strcmp(argv[i], "--version") == 0) {
838✔
1010
      global.printVersion = true;
656✔
1011
#ifdef WINDOWS
1012
    } else if (strcmp(argv[i], "--win_service") == 0) {
1013
      global.winServiceMode = true;
1014
#endif
1015
    } else if (strcmp(argv[i], "-e") == 0) {
182✔
1016
      global.envCmd[cmdEnvIndex] = argv[++i];
28✔
1017
      cmdEnvIndex++;
28✔
1018
    } else if (strcmp(argv[i], "-dm") == 0) {
154✔
1019
      global.memDbg = true;
14✔
1020
#ifdef USE_SHARED_STORAGE
1021
    } else if (strcmp(argv[i], "--checkss") == 0) {
140✔
UNCOV
1022
      global.checkSs = true;
×
1023
#endif
1024
    } else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "--usage") == 0 ||
140✔
1025
               strcmp(argv[i], "-?") == 0) {
48✔
1026
      global.printHelp = true;
92✔
1027
    } else {
1028
      printf("taosd: invalid option: %s\n", argv[i]);
48✔
1029
      printf("Try `taosd --help' or `taosd --usage' for more information.\n");
48✔
1030
      return TSDB_CODE_INVALID_CFG;
48✔
1031
    }
1032
  }
1033

1034
  return dmFinalizeRepairOption();
661,022✔
1035
}
1036

1037
static void dmPrintArgs(int32_t argc, char const *argv[]) {
660,186✔
1038
  char path[1024] = {0};
660,186✔
1039
  taosGetCwd(path, sizeof(path));
660,186✔
1040

1041
  char args[1024] = {0};
660,186✔
1042
  if (argc > 0) {
660,186✔
1043
    int32_t arglen = snprintf(args, sizeof(args), "%s", argv[0]);
660,186✔
1044
    for (int32_t i = 1; i < argc; ++i) {
1,989,734✔
1045
      arglen = arglen + snprintf(args + arglen, sizeof(args) - arglen, " %s", argv[i]);
1,329,548✔
1046
    }
1047
  }
1048

1049
  dInfo("startup path:%s args:%s", path, args);
660,186✔
1050
}
660,186✔
1051

1052
static void dmGenerateGrant() { mndGenerateMachineCode(); }
45✔
1053

1054
static void dmPrintVersion() {
576✔
1055
  printf("%s\n%sd version: %s compatible_version: %s\n", TD_PRODUCT_NAME, CUS_PROMPT, td_version,
576✔
1056
         td_compatible_version);
1057
  printf("git: %s\n", td_gitinfo);
576✔
1058
#ifdef TD_ENTERPRISE
1059
  printf("gitOfInternal: %s\n", td_gitinfoOfInternal);
576✔
1060
#endif
1061
  printf("build: %s\n", td_buildinfo);
576✔
1062
}
576✔
1063

1064
static void dmPrintHelp() {
76✔
1065
  char indent[] = "  ";
76✔
1066
  printf("Usage: %sd [OPTION...] \n\n", CUS_PROMPT);
76✔
1067
  printf("%s%s%s%s\n", indent, "-a,", indent, DM_APOLLO_URL);
76✔
1068
  printf("%s%s%s%s\n", indent, "-c,", indent, DM_CFG_DIR);
76✔
1069
  printf("%s%s%s%s\n", indent, "-s,", indent, DM_SDB_INFO);
76✔
1070
  printf("%s%s%s%s\n", indent, "-C,", indent, DM_DMP_CFG);
76✔
1071
  printf("%s%s%s%s\n", indent, "-e,", indent, DM_ENV_CMD);
76✔
1072
  printf("%s%s%s%s\n", indent, "-E,", indent, DM_ENV_FILE);
76✔
1073
  printf("%s%s%s%s\n", indent, "-r,", indent, DM_REPAIR_MODE);
76✔
1074
  printf("%s%s%s%s\n", indent, "-k,", indent, DM_MACHINE_CODE);
76✔
1075
#if defined(LINUX)
1076
  printf("%s%s%s%s\n", indent, "-o, --log-output=OUTPUT", indent, DM_LOG_OUTPUT);
76✔
1077
#endif
1078
  printf("%s%s%s%s\n", indent, "--SoD=mandatory", indent, DM_SOD_ENFORCE);
76✔
1079
  printf("%s%s%s%s\n", indent, "-y,", indent, DM_SET_ENCRYPTKEY);
76✔
1080
  printf("%s%s%s%s\n", indent, "-dm,", indent, DM_MEM_DBG);
76✔
1081
  printf("%s%s%s%s\n", indent, "-V,", indent, DM_VERSION);
76✔
1082

1083
  printf("\n\nReport bugs to %s.\n", DM_EMAIL);
76✔
1084
}
76✔
1085

1086
static void dmPrintRepairHelp() {
16✔
1087
  printf("Usage: %sd -r --mode force --node-type vnode [--backup-path PATH]\n", CUS_PROMPT);
16✔
1088
  printf("              --repair-target TARGET [--repair-target TARGET]...\n\n");
16✔
1089

1090
  printf("Current scope\n");
16✔
1091
  printf("  --node-type: vnode (only)\n");
16✔
1092
  printf("  --mode: force (only)\n");
16✔
1093
  printf("  --backup-path: optional global backup root\n");
16✔
1094
  printf("  --repair-target: <file-type>:<key>=<value>[:<key>=<value>]...\n\n");
16✔
1095

1096
  printf("Supported targets\n");
16✔
1097
  printf("  meta:vnode=<id>[:strategy=from_uid|from_redo]\n");
16✔
1098
  printf("  tsdb:vnode=<id>:fileid=<id|*>[:strategy=drop_invalid_only|head_only_rebuild|full_rebuild]\n");
16✔
1099
  printf("  wal:vnode=<id>\n");
16✔
1100
}
16✔
1101

1102
static void dmDumpCfg() {
45✔
1103
  SConfig *pCfg = taosGetCfg();
45✔
1104
  cfgDumpCfg(pCfg, 0, true);
45✔
1105
}
45✔
1106

1107
#ifdef USE_SHARED_STORAGE
UNCOV
1108
static int32_t dmCheckSs() {
×
UNCOV
1109
  int32_t code = 0;
×
UNCOV
1110
  (void)printf("\n");
×
1111

UNCOV
1112
  if (!tsSsEnabled) {
×
UNCOV
1113
    printf("shared storage is disabled (ssEnabled is 0), please enable it and try again.\n");
×
UNCOV
1114
    return TSDB_CODE_OPS_NOT_SUPPORT;
×
1115
  }
1116

1117
  code = tssInit();
×
1118
  if (code != 0) {
×
1119
    printf("failed to initialize shared storage, error code=%d.\n", code);
×
UNCOV
1120
    return code;
×
1121
  }
1122

1123
  code = tssCreateDefaultInstance();
×
UNCOV
1124
  if (code != 0) {
×
UNCOV
1125
    printf("failed to create default shared storage instance, error code=%d.\n", code);
×
1126
    (void)tssUninit();
×
1127
    return code;
×
1128
  }
1129

UNCOV
1130
  (void)printf("shared storage configuration\n");
×
UNCOV
1131
  (void)printf("=================================================================\n");
×
1132
  tssPrintDefaultConfig();
×
1133
  (void)printf("=================================================================\n");
×
1134
  code = tssCheckDefaultInstance(0);
×
1135
  (void)printf("=================================================================\n");
×
1136

UNCOV
1137
  if (code == TSDB_CODE_SUCCESS) {
×
UNCOV
1138
    printf("shared storage configuration check finished successfully.\n");
×
1139
  } else {
1140
    printf("shared storage configuration check finished with error.\n");
×
1141
  }
1142

1143
  (void)tssCloseDefaultInstance();
×
1144
  (void)tssUninit();
×
1145

1146
  return code;
×
1147
}
1148
#endif
1149

1150
static int32_t dmInitLog() {
660,229✔
1151
  const char *logName = CUS_PROMPT "dlog";
660,229✔
1152

1153
  TAOS_CHECK_RETURN(taosInitLogOutput(&logName));
660,229✔
1154

1155
  return taosCreateLog(logName, 1, configDir, global.envCmd, global.envFile, global.apolloUrl, global.pArgs, 0);
660,214✔
1156
}
1157

1158
static void taosCleanupTransientArgs() {
661,138✔
1159
  if (global.envCmd != NULL) taosMemoryFreeClear(global.envCmd);
661,138✔
1160
}
661,138✔
1161

1162
static void taosCleanupRepairArgs() {
661,138✔
1163
  dmCleanupRepairOption(&global.repairOpt);
661,138✔
1164
}
661,138✔
1165

1166
static void taosCleanupArgs() {
1,058✔
1167
  taosCleanupTransientArgs();
1,058✔
1168
  taosCleanupRepairArgs();
1,058✔
1169
}
1,058✔
1170

1171
#ifdef DM_MAIN_TESTING
1172
int32_t dmTestParseArgs(int32_t argc, char const *argv[]) { return dmParseArgs(argc, argv); }
1173
int32_t dmTestFinalizeRepairOption(void) { return dmFinalizeRepairOption(); }
1174
void    dmTestCleanupTransientArgs(void) { taosCleanupTransientArgs(); }
1175
void    dmTestCleanupRepairArgs(void) { taosCleanupRepairArgs(); }
1176
void    dmTestResetState(void) {
1177
  taosCleanupArgs();
1178
  memset(&global.repairOpt, 0, sizeof(global.repairOpt));
1179
  global.runRepairFlow = false;
1180
  global.printHelp = false;
1181
  global.printRepairHelp = false;
1182
}
1183
#endif
1184

1185
#ifdef TAOSD_INTEGRATED
1186
int dmStartDaemon(int argc, char const *argv[]) {
1187
#else
1188
int main(int argc, char const *argv[]) {
661,244✔
1189
#endif
1190
  int32_t code = 0;
661,244✔
1191
#ifdef TD_JEMALLOC_ENABLED
1192
  bool jeBackgroundThread = true;
1193
  mallctl("background_thread", NULL, NULL, &jeBackgroundThread, sizeof(bool));
1194
#endif
1195
  if (!taosCheckSystemIsLittleEnd()) {
661,244✔
UNCOV
1196
    printf("failed to start since on non-little-end machines\n");
×
UNCOV
1197
    return -1;
×
1198
  }
1199

1200
  if ((code = dmParseArgs(argc, argv)) != 0) {
661,244✔
1201
    // printf("failed to start since parse args error\n");
1202
    taosCleanupArgs();
302✔
1203
    return code;
302✔
1204
  }
1205

1206
#ifdef WINDOWS
1207
  int mainWindows(int argc, char **argv);
1208
  if (global.winServiceMode) {
1209
    stratWindowsService(mainWindows);
1210
  } else {
1211
    return mainWindows(argc, argv);
1212
  }
1213
  return 0;
1214
}
1215
int mainWindows(int argc, char **argv) {
1216
  int32_t code = 0;
1217
#endif
1218

1219
  if (global.generateGrant) {
660,942✔
1220
    dmGenerateGrant();
45✔
UNCOV
1221
    taosCleanupArgs();
×
UNCOV
1222
    return 0;
×
1223
  }
1224

1225
  if (global.printRepairHelp) {
660,897✔
1226
    dmPrintRepairHelp();
16✔
1227
    taosCleanupArgs();
16✔
1228
    return 0;
16✔
1229
  }
1230

1231
  if (global.printHelp) {
660,881✔
1232
    dmPrintHelp();
76✔
1233
    taosCleanupArgs();
76✔
1234
    return 0;
76✔
1235
  }
1236

1237
  if (global.printVersion) {
660,805✔
1238
    dmPrintVersion();
576✔
1239
    taosCleanupArgs();
576✔
1240
    return 0;
576✔
1241
  }
1242

1243
#if defined(LINUX)
1244
  if (global.memDbg) {
660,229✔
1245
    code = taosMemoryDbgInit();
14✔
1246
    if (code) {
14✔
UNCOV
1247
      printf("failed to init memory dbg, error:%s\n", tstrerror(code));
×
UNCOV
1248
      return code;
×
1249
    }
1250
    tsAsyncLog = false;
14✔
1251
    printf("memory dbg enabled\n");
14✔
1252
  }
1253
#endif
1254
  if (global.generateCode) {
660,229✔
UNCOV
1255
    bool toLogFile = false;
×
1256
    if ((code = taosReadDataFolder(configDir, global.envCmd, global.envFile, global.apolloUrl, global.pArgs)) != 0) {
×
1257
      encryptError("failed to generate encrypt code since dataDir can not be set from cfg file,reason:%s",
×
1258
                   tstrerror(code));
UNCOV
1259
      return code;
×
1260
    };
UNCOV
1261
    TdFilePtr pFile;
×
UNCOV
1262
    if ((code = dmCheckRunning(tsDataDir, &pFile)) != 0) {
×
UNCOV
1263
      encryptError("failed to generate encrypt code since taosd is running, please stop it first, reason:%s",
×
1264
                   tstrerror(code));
1265
      return code;
×
1266
    }
UNCOV
1267
    int ret = dmUpdateEncryptKey(global.encryptKey, toLogFile);
×
1268
    if (taosCloseFile(&pFile) != 0) {
×
UNCOV
1269
      encryptError("failed to close file:%p", pFile);
×
1270
    }
1271
    taosCloseLog();
×
1272
    taosCleanupArgs();
×
UNCOV
1273
    return ret;
×
1274
  }
1275

1276
  if ((code = dmInitLog()) != 0) {
660,229✔
1277
    printf("failed to start since init log error\n");
43✔
1278
    taosCleanupArgs();
43✔
1279
    return code;
43✔
1280
  }
1281

1282
  dmPrintArgs(argc, argv);
660,186✔
1283
  if ((code = taosPreLoadCfg(configDir, global.envCmd, global.envFile, global.apolloUrl, global.pArgs, 0)) != 0) {
660,186✔
UNCOV
1284
    dError("failed to start since pre load config error");
×
UNCOV
1285
    taosCloseLog();
×
UNCOV
1286
    taosCleanupArgs();
×
UNCOV
1287
    return code;
×
1288
  }
1289

1290
  if ((code = dmGetEncryptKey()) != 0) {
660,186✔
UNCOV
1291
    dError("failed to start since failed to get encrypt key");
×
UNCOV
1292
    taosCloseLog();
×
1293
    taosCleanupArgs();
×
1294
    return code;
×
1295
  };
1296

1297
  if ((code = tryLoadCfgFromDataDir(tsCfg)) != 0) {
660,186✔
UNCOV
1298
    dError("failed to start since try load config from data dir error");
×
UNCOV
1299
    taosCloseLog();
×
1300
    taosCleanupArgs();
×
1301
    return code;
×
1302
  }
1303

1304
  if ((code = taosApplyCfg(configDir, global.envCmd, global.envFile, global.apolloUrl, global.pArgs, 0)) != 0) {
660,186✔
UNCOV
1305
    dError("failed to start since apply config error");
×
UNCOV
1306
    taosCloseLog();
×
1307
    taosCleanupArgs();
×
1308
    return code;
×
1309
  }
1310

1311
  if ((code = taosMemoryPoolInit(qWorkerRetireJobs, qWorkerRetireJob)) != 0) {
660,186✔
UNCOV
1312
    dError("failed to init memPool, error:0x%x", code);
×
UNCOV
1313
    taosCloseLog();
×
1314
    taosCleanupArgs();
×
1315
    return code;
×
1316
  }
1317

1318
#ifndef DISALLOW_NCHAR_WITHOUT_ICONV
1319
  if ((tsCharsetCxt = taosConvInit(tsCharset)) == NULL) {
660,186✔
UNCOV
1320
    dError("failed to init conv");
×
1321
    taosCloseLog();
×
1322
    taosCleanupArgs();
×
1323
    return code;
×
1324
  }
1325
#endif
1326

1327
#ifdef USE_SHARED_STORAGE
1328
  if (global.checkSs) {
660,186✔
1329
    code = dmCheckSs();
×
1330
    taosCleanupCfg();
×
1331
    taosCloseLog();
×
1332
    taosCleanupArgs();
×
UNCOV
1333
    taosConvDestroy();
×
UNCOV
1334
    return code;
×
1335
  }
1336
#endif
1337

1338
  if (global.dumpConfig) {
660,186✔
1339
    dmDumpCfg();
45✔
1340
    taosCleanupCfg();
45✔
1341
    taosCloseLog();
45✔
1342
    taosCleanupArgs();
45✔
1343
    taosConvDestroy();
45✔
1344
    return 0;
45✔
1345
  }
1346

1347
  if (global.dumpSdb) {
660,141✔
1348
    int32_t code = 0;
61✔
1349
    tsSkipKeyCheckMode = true;  // Set global flag to skip key check mode
61✔
1350
    TAOS_CHECK_RETURN(mndDumpSdb());
61✔
UNCOV
1351
    taosCleanupCfg();
×
UNCOV
1352
    taosCloseLog();
×
UNCOV
1353
    taosCleanupArgs();
×
UNCOV
1354
    taosConvDestroy();
×
UNCOV
1355
    return 0;
×
1356
  }
1357

1358
  if (global.deleteTrans) {
660,080✔
UNCOV
1359
    int32_t   code = 0;
×
1360
    TdFilePtr pFile;
×
1361
    if ((code = dmCheckRunning(tsDataDir, &pFile)) != 0) {
×
1362
      printf("failed to generate encrypt code since taosd is running, please stop it first, reason:%s",
×
1363
             tstrerror(code));
1364
      return code;
×
1365
    }
1366

UNCOV
1367
    TAOS_CHECK_RETURN(mndDeleteTrans());
×
1368
    taosCleanupCfg();
×
1369
    taosCloseLog();
×
1370
    taosCleanupArgs();
×
1371
    taosConvDestroy();
×
UNCOV
1372
    return 0;
×
1373
  }
1374

1375
  if (global.modifySdb) {
660,080✔
1376
    int32_t   code = 0;
×
1377
    TdFilePtr pFile;
×
1378
    if ((code = dmCheckRunning(tsDataDir, &pFile)) != 0) {
×
1379
      printf("failed to modify sdb since taosd is running, please stop it first, reason:%s", tstrerror(code));
×
1380
      return code;
×
1381
    }
1382

UNCOV
1383
    TAOS_CHECK_RETURN(mndModifySdb(global.sdbJsonFile));
×
UNCOV
1384
    taosCleanupCfg();
×
1385
    taosCloseLog();
×
1386
    taosCleanupArgs();
×
1387
    taosConvDestroy();
×
1388
    return 0;
×
1389
  }
1390

1391
  osSetProcPath(argc, (char **)argv);
660,080✔
1392
  taosCleanupTransientArgs();
660,080✔
1393

1394
  if (tsEncryptExtDir[0] != '\0') {
660,080✔
1395
#if defined(TD_ENTERPRISE) && defined(LINUX)
1396
    if ((code = cryptLoadProviders()) != 0) {
×
1397
      dError("failed to load encrypt providers since %s", tstrerror(code));
×
UNCOV
1398
      taosCloseLog();
×
UNCOV
1399
      taosCleanupRepairArgs();
×
UNCOV
1400
      return code;
×
1401
    }
1402
#endif
1403
  }
1404

1405
  if ((code = dmInit()) != 0) {
660,080✔
1406
    if (code == TSDB_CODE_NOT_FOUND) {
11,183✔
1407
      dError(
×
1408
          "Initialization of dnode failed because your current operating system is not supported. For more information "
1409
          "and supported platforms, please visit https://docs.taosdata.com/reference/supported/.");
1410
    } else {
1411
      dError("failed to init dnode since %s", tstrerror(code));
11,183✔
1412
    }
1413

1414
    taosCleanupCfg();
11,183✔
1415
    taosCloseLog();
11,183✔
1416
    taosConvDestroy();
11,183✔
1417
    taosCleanupRepairArgs();
11,183✔
1418
    return code;
11,183✔
1419
  }
1420

1421
  dInfo("start to init service");
648,897✔
1422
  dmSetSignalHandle();
648,897✔
1423

1424
  code = dmRun();
648,897✔
1425
  dInfo("shutting down the service");
648,897✔
1426

1427
  dmCleanup();
648,897✔
1428
  taosCleanupRepairArgs();
648,897✔
1429
  return code;
648,897✔
1430
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc