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

taosdata / TDengine / #4935

22 Jan 2026 06:38AM UTC coverage: 66.708% (+0.02%) from 66.691%
#4935

push

travis-ci

web-flow
merge: from main to 3.0 #34371

121 of 271 new or added lines in 17 files covered. (44.65%)

9066 existing lines in 149 files now uncovered.

203884 of 305637 relevant lines covered (66.71%)

125811266.68 hits per line

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

64.02
/source/libs/txnode/src/txnodeMgmt.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

16
// clang-format off
17
#include "uv.h"
18
#include "os.h"
19
#include "tarray.h"
20
#include "tglobal.h"
21
#include "txnode.h"
22
#include "txnodeInt.h"
23
#include "osString.h"
24

25
// clang-format on
26

27
extern char **environ;
28

29
#ifdef WINDOWS
30
#define XNODED_DEFAULT_PATH_1    "C:\\TDengine"
31
#define XNODED_DEFAULT_PATH_2    "C:\\TDengine"
32
#define XNODED_DEFAULT_EXEC_NAME "xnoded"
33
#define XNODED_DEFAULT_EXEC      "\\xnoded.exe"
34
#else
35
#define XNODED_DEFAULT_PATH_1    "/usr/bin"
36
#define XNODED_DEFAULT_PATH_2    "/usr/local/taos/bin"
37
#define XNODED_DEFAULT_EXEC_NAME "xnoded"
38
#define XNODED_DEFAULT_EXEC      "/xnoded"
39
#endif
40

41
#define XNODED_XNODED_PID_NAME ".xnoded.pid"
42

43
typedef struct {
44
  bool         isStarted;
45
  bool         needCleanUp;
46
  uv_loop_t    loop;
47
  uv_thread_t  thread;
48
  uv_barrier_t barrier;
49
  uv_process_t process;
50
  int32_t      spawnErr;
51
  uv_pipe_t    ctrlPipe;
52
  uv_async_t   stopAsync;
53
  int32_t      isStopped;
54
  int32_t      dnodeId;
55
  int64_t      clusterId;
56
  char         userPass[XNODE_USER_PASS_LEN];
57
  SEp          leaderEp;
58
} SXnodedData;
59

60
SXnodedData xnodedGlobal = {0};
61

62
static int32_t xnodeMgmtSpawnXnoded(SXnodedData *pData);
63

64
static void getXnodedPidPath(char *pipeName, int32_t size) {
442✔
65
#ifdef _WIN32
66
  snprintf(pipeName, size, "%s%s", tsDataDir, XNODED_XNODED_PID_NAME);
67
#else
68
  snprintf(pipeName, size, "%s%s", tsDataDir, XNODED_XNODED_PID_NAME);
442✔
69
#endif
70
  xndDebug("xnode get xnoded pid path:%s", pipeName);
442✔
71
}
442✔
72

73
static void    xnodeMgmtXnodedExit(uv_process_t *process, int64_t exitStatus, int32_t termSignal) {
×
74
  TAOS_XNODED_MGMT_CHECK_PTR_RVOID(process);
×
75
  xndDebug("xnoded process exited with status %" PRId64 ", signal %d", exitStatus, termSignal);
×
UNCOV
76
  SXnodedData *pData = process->data;
×
77
  if (pData == NULL) {
×
78
    xndError("xnoded process data is NULL");
×
79
    return;
×
80
  }
UNCOV
81
  if ((exitStatus == 0 && termSignal == 0) || atomic_load_32(&pData->isStopped)) {
×
82
    xndInfo("xnoded process exit due to exit status 0 or dnode-mgmt called stop");
×
83
    if (uv_async_send(&pData->stopAsync) != 0) {
×
84
      xndError("stop xnoded: failed to send stop async");
×
85
    }
UNCOV
86
    char xnodedPipeSocket[PATH_MAX] = {0};
×
UNCOV
87
    getXnodedPipeName(xnodedPipeSocket, PATH_MAX);
×
88
    if (0 != unlink(xnodedPipeSocket)) {
×
89
      xndWarn("txnode failed to unlink, socket:%s, err:%s", xnodedPipeSocket, terrstr());
×
90
    }
91

UNCOV
92
    char *pidPath = xnodedPipeSocket;
×
93
    memset(pidPath, 0, PATH_MAX);
×
94
    getXnodedPidPath(pidPath, PATH_MAX);
×
95
    (void)taosRemoveFile(pidPath);
×
96
  } else {
97
    xndInfo("xnoded process restart, exit status %" PRId64 ", signal %d", exitStatus, termSignal);
×
UNCOV
98
    uv_sleep(2000);
×
UNCOV
99
    int32_t code = xnodeMgmtSpawnXnoded(pData);
×
UNCOV
100
    if (code != 0) {
×
UNCOV
101
      xndError("xnoded process restart failed with code:%d", code);
×
102
    }
103
  }
104
}
105
void killPreXnoded() {
442✔
106
  char buf[PATH_MAX] = {0};
442✔
107
  getXnodedPidPath(buf, sizeof(buf));
442✔
108

109
  TdFilePtr pFile = NULL;
442✔
110
  pFile = taosOpenFile(buf, TD_FILE_READ);
442✔
111
  if (pFile == NULL) {
442✔
112
    xndWarn("xnode failed to open xnoded pid file:%s, file may not exist", buf);
442✔
113
    return;
442✔
114
  }
UNCOV
115
  int64_t readSize = taosReadFile(pFile, buf, sizeof(buf));
×
116
  if (readSize <= 0) {
×
117
    if (readSize < 0) {
×
UNCOV
118
      xndError("xnode failed to read len from file:%p since %s", pFile, terrstr());
×
119
    }
UNCOV
120
    (void)taosCloseFile(&pFile);
×
121
    return;
×
122
  }
123
  (void)taosCloseFile(&pFile);
×
124

125
  int32_t pid = taosStr2Int32(buf, NULL, 10);
×
UNCOV
126
  int result = uv_kill((uv_pid_t)pid, SIGTERM);
×
127
  if (result != 0) {
×
UNCOV
128
    if (result != UV_ESRCH) {
×
UNCOV
129
      xndError("xnode failed to kill process %d: %s", pid, uv_strerror(result));
×
130
    }
131
    return;
×
132
  }
133
}
134

135
void saveXnodedPid(int32_t pid) {
×
136
  char buf[PATH_MAX] = {0};
×
137
  getXnodedPidPath(buf, sizeof(buf));
×
138
  TdFilePtr testFilePtr = taosCreateFile(buf, TD_FILE_CREATE | TD_FILE_WRITE | TD_FILE_READ | TD_FILE_TRUNC);
×
UNCOV
139
  snprintf(buf, PATH_MAX, "%d", pid);
×
UNCOV
140
  (void)taosWriteFile(testFilePtr, buf, strlen(buf));
×
UNCOV
141
  (void)taosCloseFile(&testFilePtr);
×
UNCOV
142
}
×
143

144
static void locateXnodedExecFile(char *path) {
442✔
145
  if (tsProcPath == NULL) {
442✔
UNCOV
146
    path[0] = '.';
×
147
#ifdef WINDOWS
148
    GetModuleFileName(NULL, path, PATH_MAX);
149
#elif defined(_TD_DARWIN_64)
150
    uint32_t pathSize = sizeof(path);
151
    _NSGetExecutablePath(path, &pathSize);
152
#endif
153
  } else {
154
    TAOS_STRNCPY(path, tsProcPath, PATH_MAX);
442✔
155
  }
156

157
  TAOS_DIRNAME(path);
442✔
158
  if (strlen(path) != 0) {
442✔
159
    TAOS_STRCAT(path, XNODED_DEFAULT_EXEC);
442✔
160
    if (taosCheckExistFile(path)) {
442✔
UNCOV
161
      goto _ok;
×
162
    }
163
    xndDebug("can't find xnoded exec file:%s", path);
442✔
164
    path[0] = '\0';
442✔
165
  }
166

167
  TAOS_STRCAT(path, XNODED_DEFAULT_PATH_1);
442✔
168
  TAOS_STRCAT(path, XNODED_DEFAULT_EXEC);
442✔
169
  if (taosCheckExistFile(path)) {
442✔
UNCOV
170
    goto _ok;
×
171
  }
172
  xndDebug("can't find xnoded exec file:%s", path);
442✔
173
  path[0] = '\0';
442✔
174

175
  TAOS_STRCAT(path, XNODED_DEFAULT_PATH_2);
442✔
176
  TAOS_STRCAT(path, XNODED_DEFAULT_EXEC);
442✔
177
  if (taosCheckExistFile(path)) {
442✔
UNCOV
178
    goto _ok;
×
179
  }
180
  xndDebug("can't find xnoded exec file:%s", path);
442✔
181
  path[0] = '\0';
442✔
182

183
  path[0] = '.';
442✔
184
  path[1] = '\0';
442✔
185
  TAOS_STRCAT(path, XNODED_DEFAULT_EXEC);
442✔
186
  if (taosCheckExistFile(path)) {
442✔
UNCOV
187
    goto _ok;
×
188
  }
189
  xndDebug("can't find xnoded exec file:%s", path);
442✔
190
  path[0] = '\0';
442✔
191

192
  TAOS_STRNCPY(path, XNODED_DEFAULT_EXEC_NAME, PATH_MAX);
442✔
193
  xndInfo("can't find xnoded file, use default exec command:%s", path);
442✔
194

UNCOV
195
_ok:
×
196
  xndInfo("find xnoded exec file:%s", path);
442✔
197
  return;
442✔
198
}
199

200
static int32_t xnodeMgmtSpawnXnoded(SXnodedData *pData) {
442✔
201
  xndDebug("start to init xnoded");
442✔
202
  TAOS_XNODED_MGMT_CHECK_PTR_RCODE(pData);
884✔
203

204
  int32_t              err = 0;
442✔
205
  uv_process_options_t options = {0};
442✔
206

207
  char path[PATH_MAX] = {0};
442✔
208
  locateXnodedExecFile(path);
442✔
209

210
  xndInfo("xnode mgmt spawn xnoded path: %s", path);
442✔
211
  // char *argsXnoded[] = {path, "-c", configDir, "-d", dnodeId, NULL};
212
  char *argsXnoded[] = {path, NULL};
442✔
213
  options.args = argsXnoded;
442✔
214
  options.file = path;
442✔
215

216
  options.exit_cb = xnodeMgmtXnodedExit;
442✔
217

218
  killPreXnoded();
442✔
219

220
  char xnodedPipeSocket[PATH_MAX] = {0};
442✔
221
  getXnodedPipeName(xnodedPipeSocket, PATH_MAX);
442✔
222
  if (0 != unlink(xnodedPipeSocket)) {
442✔
223
    xndWarn("txnode failed to unlink, ignore if first time, socket:%s, err:%s", xnodedPipeSocket, terrstr());
442✔
224
  }
225

226
  TAOS_UV_LIB_ERROR_RET(uv_pipe_init(&pData->loop, &pData->ctrlPipe, 1));
442✔
227

228
  uv_stdio_container_t child_stdio[3];
442✔
229
  child_stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE;
442✔
230
  child_stdio[0].data.stream = (uv_stream_t *)&pData->ctrlPipe;
442✔
231
  child_stdio[1].flags = UV_IGNORE;
442✔
232
  child_stdio[2].flags = UV_INHERIT_FD;
442✔
233
  child_stdio[2].data.fd = 2;
442✔
234
  options.stdio_count = 3;
442✔
235
  options.stdio = child_stdio;
442✔
236

237
  options.flags = UV_PROCESS_DETACHED;
442✔
238

239
  char xnodedCfgDir[PATH_MAX] = {0};
442✔
240
  snprintf(xnodedCfgDir, PATH_MAX, "%s=%s", "XNODED_CFG_DIR", configDir);
442✔
241
  char xnodedLogDir[PATH_MAX] = {0};
442✔
242
  snprintf(xnodedLogDir, PATH_MAX, "%s=%s", "XNODED_LOG_DIR", tsLogDir);
442✔
243
  char dnodeIdEnvItem[64] = {0};
442✔
244
  snprintf(dnodeIdEnvItem, 64, "%s=%s:%d", "XNODED_LEADER_EP", pData->leaderEp.fqdn, pData->leaderEp.port);
442✔
245
  char xnodedUserPass[XNODE_USER_PASS_LEN] = {0};
442✔
246
  snprintf(xnodedUserPass, XNODE_USER_PASS_LEN, "%s=%s", "XNODED_USER_PASS", pData->userPass);
442✔
247
  char xnodeClusterId[32] = {0};
442✔
248
  snprintf(xnodeClusterId, 32, "%s=%" PRIu64, "XNODED_CLUSTER_ID", pData->clusterId);
442✔
249
  char xnodePipeSocket[PATH_MAX + 64] = {0};
442✔
250
  snprintf(xnodePipeSocket, PATH_MAX + 64, "%s=%s", "XNODED_LISTEN", xnodedPipeSocket);
442✔
251

252
  char xnodedLogLevel[32] = {0};
442✔
253
  if (xndDebugFlag & DEBUG_INFO) {
442✔
254
    snprintf(xnodedLogLevel, 32, "%s=%s", "XNODED_LOG_LEVEL", "info");
442✔
255
  }
256
  if (xndDebugFlag & DEBUG_DEBUG) {
442✔
UNCOV
257
    snprintf(xnodedLogLevel, 32, "%s=%s", "XNODED_LOG_LEVEL", "debug");
×
258
  }
259
  if (xndDebugFlag & DEBUG_TRACE) {
442✔
UNCOV
260
    snprintf(xnodedLogLevel, 32, "%s=%s", "XNODED_LOG_LEVEL", "trace");
×
261
  }
262

263
  xndDebug("txnode env: leader ep: %s, user pass:%s, pipe socket:%s, log level:%s", dnodeIdEnvItem, xnodedUserPass,
442✔
264
           xnodePipeSocket, xnodedLogLevel);
265

266
  char *envXnoded[] = {xnodedCfgDir,   xnodedLogDir,    dnodeIdEnvItem, xnodedUserPass,
442✔
267
                       xnodeClusterId, xnodePipeSocket, xnodedLogLevel, NULL};
268

269
  char **envXnodedWithPEnv = NULL;
442✔
270
  if (environ != NULL) {
442✔
271
    int32_t lenEnvXnoded = ARRAY_SIZE(envXnoded);
442✔
272
    int32_t numEnviron = 0;
442✔
273
    while (environ[numEnviron] != NULL) {
13,702✔
274
      numEnviron++;
13,260✔
275
    }
276

277
    envXnodedWithPEnv = (char **)taosMemoryCalloc(numEnviron + lenEnvXnoded, sizeof(char *));
442✔
278
    if (envXnodedWithPEnv == NULL) {
442✔
UNCOV
279
      err = TSDB_CODE_OUT_OF_MEMORY;
×
UNCOV
280
      goto _OVER;
×
281
    }
282

283
    for (int32_t i = 0; i < numEnviron; i++) {
13,702✔
284
      int32_t len = strlen(environ[i]) + 1;
13,260✔
285
      xndDebug("xnoded exec env: %s", environ[i]);
13,260✔
286
      envXnodedWithPEnv[i] = (char *)taosMemoryCalloc(len, 1);
13,260✔
287
      if (envXnodedWithPEnv[i] == NULL) {
13,260✔
288
        err = TSDB_CODE_OUT_OF_MEMORY;
×
UNCOV
289
        goto _OVER;
×
290
      }
291

292
      tstrncpy(envXnodedWithPEnv[i], environ[i], len);
13,260✔
293
    }
294

295
    for (int32_t i = 0; i < lenEnvXnoded; i++) {
3,978✔
296
      if (envXnoded[i] != NULL) {
3,536✔
297
        int32_t len = strlen(envXnoded[i]) + 1;
3,094✔
298
        envXnodedWithPEnv[numEnviron + i] = (char *)taosMemoryCalloc(len, 1);
3,094✔
299
        if (envXnodedWithPEnv[numEnviron + i] == NULL) {
3,094✔
UNCOV
300
          err = TSDB_CODE_OUT_OF_MEMORY;
×
UNCOV
301
          goto _OVER;
×
302
        }
303

304
        tstrncpy(envXnodedWithPEnv[numEnviron + i], envXnoded[i], len);
3,094✔
305
      }
306
    }
307
    envXnodedWithPEnv[numEnviron + lenEnvXnoded - 1] = NULL;
442✔
308

309
    options.env = envXnodedWithPEnv;
442✔
310
  } else {
311
    options.env = envXnoded;
×
312
  }
313

314
  err = uv_spawn(&pData->loop, &pData->process, &options);
442✔
315
  pData->process.data = (void *)pData;
442✔
316
  if (err != 0) {
442✔
317
    xndError("can not spawn xnoded. path: %s, error: %s", path, uv_strerror(err));
442✔
318
  } else {
UNCOV
319
    xndInfo("xnoded is initialized, xnoded pid: %d", pData->process.pid);
×
UNCOV
320
    saveXnodedPid(pData->process.pid);
×
321
  }
322

323
_OVER:
442✔
324
  // if (taosFqdnEnvItem) {
325
  //   taosMemoryFree(taosFqdnEnvItem);
326
  // }
327

328
  if (envXnodedWithPEnv != NULL) {
442✔
329
    int32_t i = 0;
442✔
330
    while (envXnodedWithPEnv[i] != NULL) {
16,796✔
331
      taosMemoryFree(envXnodedWithPEnv[i]);
16,354✔
332
      i++;
16,354✔
333
    }
334
    taosMemoryFree(envXnodedWithPEnv);
442✔
335
  }
336

337
  return err;
442✔
338
}
339

UNCOV
340
static void xnodeMgmtXnodedCloseWalkCb(uv_handle_t *handle, void *arg) {
×
UNCOV
341
  TAOS_XNODED_MGMT_CHECK_PTR_RVOID(handle);
×
342
  if (!uv_is_closing(handle)) {
×
343
    uv_close(handle, NULL);
×
344
  }
345
}
346

UNCOV
347
static void xnodeMgmtXnodedStopAsyncCb(uv_async_t *async) {
×
UNCOV
348
  TAOS_XNODED_MGMT_CHECK_PTR_RVOID(async);
×
UNCOV
349
  SXnodedData *pData = async->data;
×
UNCOV
350
  uv_stop(&pData->loop);
×
351
}
352

353
static void xnodeMgmtWatchXnoded(void *args) {
221✔
354
  TAOS_XNODED_MGMT_CHECK_PTR_RVOID(args);
442✔
355
  SXnodedData *pData = args;
221✔
356
  TAOS_UV_CHECK_ERRNO(uv_loop_init(&pData->loop));
221✔
357
  TAOS_UV_CHECK_ERRNO(uv_async_init(&pData->loop, &pData->stopAsync, xnodeMgmtXnodedStopAsyncCb));
221✔
358
  pData->stopAsync.data = pData;
221✔
359
  TAOS_UV_CHECK_ERRNO(xnodeMgmtSpawnXnoded(pData));
221✔
UNCOV
360
  atomic_store_32(&pData->spawnErr, 0);
×
361
  (void)uv_barrier_wait(&pData->barrier);
×
UNCOV
362
  int32_t num = uv_run(&pData->loop, UV_RUN_DEFAULT);
×
UNCOV
363
  xndInfo("xnoded loop exit with %d active handles, line:%d", num, __LINE__);
×
364

UNCOV
365
  uv_walk(&pData->loop, xnodeMgmtXnodedCloseWalkCb, NULL);
×
UNCOV
366
  num = uv_run(&pData->loop, UV_RUN_DEFAULT);
×
UNCOV
367
  xndInfo("xnoded loop exit with %d active handles, line:%d", num, __LINE__);
×
UNCOV
368
  if (uv_loop_close(&pData->loop) != 0) {
×
UNCOV
369
    xndError("xnoded loop close failed, lino:%d", __LINE__);
×
370
  }
UNCOV
371
  return;
×
372

373
_exit:
221✔
374
  if (terrno != 0) {
221✔
375
    (void)uv_barrier_wait(&pData->barrier);
221✔
376
    atomic_store_32(&pData->spawnErr, terrno);
221✔
377
    if (uv_loop_close(&pData->loop) != 0) {
221✔
378
      xndError("xnoded loop close failed, lino:%d", __LINE__);
221✔
379
    }
380

381
    xndError("xnoded thread exit with code:%d lino:%d", terrno, __LINE__);
221✔
382
    terrno = TSDB_CODE_XNODE_UV_EXEC_FAILURE;
221✔
383
  }
384
}
385

386
/**
387
 * start xnoded that serves xnode function invocation under dnode startDnodeId
388
 * @param startDnodeId
389
 * @return
390
 */
391
int32_t xnodeMgmtStartXnoded(SXnode *pXnode) {
221✔
392
  int32_t code = 0, lino = 0;
221✔
393

394
  SXnodedData *pData = &xnodedGlobal;
221✔
395
  pData->leaderEp = pXnode->ep;
221✔
396
  if (pData->isStarted) {
221✔
UNCOV
397
    xndInfo("dnode start xnoded already called");
×
UNCOV
398
    return 0;
×
399
  }
400
  pData->isStarted = true;
221✔
401
  char dnodeId[8] = {0};
221✔
402
  snprintf(dnodeId, sizeof(dnodeId), "%d", pXnode->dnodeId);
221✔
403
  TAOS_CHECK_GOTO(uv_os_setenv("DNODE_ID", dnodeId), &lino, _exit);
221✔
404
  pData->dnodeId = pXnode->dnodeId;
221✔
405
  pData->clusterId = pXnode->clusterId;
221✔
406
  memset(pData->userPass, 0, sizeof(pData->userPass));
221✔
407
  memcpy(pData->userPass, pXnode->userPass, pXnode->upLen);
221✔
408

409
  TAOS_CHECK_GOTO(uv_barrier_init(&pData->barrier, 2), &lino, _exit);
221✔
410
  TAOS_CHECK_GOTO(uv_thread_create(&pData->thread, xnodeMgmtWatchXnoded, pData), &lino, _exit);
221✔
411
  (void)uv_barrier_wait(&pData->barrier);
221✔
412
  int32_t err = atomic_load_32(&pData->spawnErr);
221✔
413
  if (err != 0) {
221✔
414
    uv_barrier_destroy(&pData->barrier);
221✔
415
    if (uv_async_send(&pData->stopAsync) != 0) {
221✔
UNCOV
416
      xndError("start xnoded: failed to send stop async");
×
417
    }
418
    if (uv_thread_join(&pData->thread) != 0) {
221✔
UNCOV
419
      xndError("start xnoded: failed to join xnoded thread");
×
420
    }
421
    pData->needCleanUp = false;
221✔
422
    xndInfo("xnoded is cleaned up after spawn err");
221✔
423
    TAOS_CHECK_GOTO(err, &lino, _exit);
221✔
424
  } else {
UNCOV
425
    pData->needCleanUp = true;
×
UNCOV
426
    atomic_store_32(&pData->isStopped, 0);
×
427
  }
428
_exit:
221✔
429
  if (code != 0) {
221✔
430
    xndError("xnoded start failed with lino:%d, code:%d, error: %s", code, lino, uv_strerror(code));
221✔
431
  }
432
  return code;
221✔
433
}
434
/**
435
 * stop xnoded
436
 * @return
437
 */
438
void xnodeMgmtStopXnoded(void) {
636,008✔
439
  SXnodedData *pData = &xnodedGlobal;
636,008✔
440
  xndInfo("stopping xnoded, need cleanup:%d, spawn err:%d", pData->needCleanUp, pData->spawnErr);
636,008✔
441
  if (!pData->needCleanUp || atomic_load_32(&pData->isStopped)) {
636,008✔
442
    return;
636,008✔
443
  }
UNCOV
444
  atomic_store_32(&pData->isStopped, 1);
×
UNCOV
445
  pData->needCleanUp = false;
×
UNCOV
446
  (void)uv_process_kill(&pData->process, SIGTERM);
×
UNCOV
447
  uv_barrier_destroy(&pData->barrier);
×
448

UNCOV
449
  if (uv_thread_join(&pData->thread) != 0) {
×
UNCOV
450
    xndError("stop xnoded: failed to join xnoded thread");
×
451
  }
UNCOV
452
  xndInfo("xnoded is cleaned up");
×
453

UNCOV
454
  pData->isStarted = false;
×
455

UNCOV
456
  return;
×
457
}
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