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

OISF / suricata / 23374838686

21 Mar 2026 07:29AM UTC coverage: 59.341% (-20.0%) from 79.315%
23374838686

Pull #15075

github

web-flow
Merge 90b4e834f into 6587e363a
Pull Request #15075: Stack 8001 v16.4

38 of 70 new or added lines in 10 files covered. (54.29%)

34165 existing lines in 563 files now uncovered.

119621 of 201584 relevant lines covered (59.34%)

650666.92 hits per line

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

1.19
/src/unix-manager.c
1
/* Copyright (C) 2013-2018 Open Information Security Foundation
2
 *
3
 * You can copy, redistribute or modify this Program under the terms of
4
 * the GNU General Public License version 2 as published by the Free
5
 * Software Foundation.
6
 *
7
 * This program is distributed in the hope that it will be useful,
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 * GNU General Public License for more details.
11
 *
12
 * You should have received a copy of the GNU General Public License
13
 * version 2 along with this program; if not, write to the Free Software
14
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15
 * 02110-1301, USA.
16
 */
17

18
/**
19
 * \file
20
 *
21
 * \author Eric Leblond <eric@regit.org>
22
 */
23

24
#include "suricata-common.h"
25
#include "unix-manager.h"
26
#include "threads.h"
27
#include "detect-engine.h"
28
#include "tm-threads.h"
29
#include "runmodes.h"
30
#include "conf.h"
31
#include "runmode-unix-socket.h"
32

33
#include "output-json-stats.h"
34

35
#include "util-conf.h"
36
#include "util-privs.h"
37
#include "util-debug.h"
38
#include "util-device-private.h"
39
#include "util-ebpf.h"
40
#include "util-signal.h"
41
#include "util-buffer.h"
42
#include "util-path.h"
43
#include "util-profiling.h"
44

45
#if (defined BUILD_UNIX_SOCKET) && (defined HAVE_SYS_UN_H) && (defined HAVE_SYS_STAT_H) && (defined HAVE_SYS_TYPES_H)
46
#include <sys/un.h>
47
#include <sys/stat.h>
48
#include <sys/types.h>
49

50
#include "output.h"
51
#include "output-json.h"
52

53
// MSG_NOSIGNAL does not exists on OS X
54
#ifdef OS_DARWIN
55
# ifndef MSG_NOSIGNAL
56
#   define MSG_NOSIGNAL SO_NOSIGPIPE
57
# endif
58
#endif
59

UNCOV
60
#define SOCKET_PATH LOCAL_STATE_DIR "/run/suricata/"
×
UNCOV
61
#define SOCKET_FILENAME "suricata-command.socket"
×
UNCOV
62
#define SOCKET_TARGET SOCKET_PATH SOCKET_FILENAME
×
63

64
SCCtrlCondT unix_manager_ctrl_cond;
65
SCCtrlMutex unix_manager_ctrl_mutex;
66

67
#define MAX_FAILED_RULES   20
×
68

69
typedef struct Command_ {
70
    char *name;
71
    TmEcode (*Func)(json_t *, json_t *, void *);
72
    void *data;
73
    int flags;
74
    TAILQ_ENTRY(Command_) next;
75
} Command;
76

77
typedef struct Task_ {
78
    TmEcode (*Func)(void *);
79
    void *data;
80
    TAILQ_ENTRY(Task_) next;
81
} Task;
82

UNCOV
83
#define CLIENT_BUFFER_SIZE 4096
×
84
typedef struct UnixClient_ {
85
    int fd;
86
    MemBuffer *mbuf; /**< buffer for response construction */
87
    int version;
88
    TAILQ_ENTRY(UnixClient_) next;
89
} UnixClient;
90

91
typedef struct UnixCommand_ {
92
    time_t start_timestamp;
93
    int socket;
94
    struct sockaddr_un client_addr;
95
    int select_max;
96
    TAILQ_HEAD(, Command_) commands;
97
    TAILQ_HEAD(, Task_) tasks;
98
    TAILQ_HEAD(, UnixClient_) clients;
99
} UnixCommand;
100

101
/**
102
 * \brief Create a command unix socket on system
103
 *
104
 * \retval 0 in case of error, 1 in case of success
105
 */
106
static int UnixNew(UnixCommand * this)
UNCOV
107
{
×
UNCOV
108
    struct sockaddr_un addr;
×
UNCOV
109
    socklen_t len;
×
UNCOV
110
    int ret;
×
UNCOV
111
    int on = 1;
×
UNCOV
112
    char sockettarget[PATH_MAX];
×
UNCOV
113
    const char *socketname;
×
114

UNCOV
115
    this->start_timestamp = time(NULL);
×
UNCOV
116
    this->socket = -1;
×
UNCOV
117
    this->select_max = 0;
×
118

UNCOV
119
    TAILQ_INIT(&this->commands);
×
UNCOV
120
    TAILQ_INIT(&this->tasks);
×
UNCOV
121
    TAILQ_INIT(&this->clients);
×
122

UNCOV
123
    int check_dir = 0;
×
UNCOV
124
    if (SCConfGet("unix-command.filename", &socketname) == 1) {
×
UNCOV
125
        if (PathIsAbsolute(socketname)) {
×
UNCOV
126
            strlcpy(sockettarget, socketname, sizeof(sockettarget));
×
UNCOV
127
        } else {
×
128
            snprintf(sockettarget, sizeof(sockettarget), "%s/%s",
×
129
                    SOCKET_PATH, socketname);
×
130
            check_dir = 1;
×
131
        }
×
UNCOV
132
    } else {
×
UNCOV
133
        strlcpy(sockettarget, SOCKET_TARGET, sizeof(sockettarget));
×
UNCOV
134
        check_dir = 1;
×
UNCOV
135
    }
×
UNCOV
136
    SCLogInfo("unix socket '%s'", sockettarget);
×
137

UNCOV
138
    if (check_dir) {
×
UNCOV
139
        struct stat stat_buf;
×
140
        /* coverity[toctou] */
UNCOV
141
        if (stat(SOCKET_PATH, &stat_buf) != 0) {
×
142
            /* coverity[toctou] */
UNCOV
143
            ret = SCMkDir(SOCKET_PATH, S_IRWXU|S_IXGRP|S_IRGRP);
×
UNCOV
144
            if (ret != 0) {
×
145
                int err = errno;
×
146
                if (err != EEXIST) {
×
147
                    SCLogError(
×
148
                            "failed to create socket directory %s: %s", SOCKET_PATH, strerror(err));
×
149
                    return 0;
×
150
                }
×
UNCOV
151
            } else {
×
UNCOV
152
                SCLogInfo("created socket directory %s", SOCKET_PATH);
×
UNCOV
153
            }
×
UNCOV
154
        }
×
UNCOV
155
    }
×
156

157
    /* Remove socket file */
UNCOV
158
    (void) unlink(sockettarget);
×
159

160
    /* set address */
UNCOV
161
    addr.sun_family = AF_UNIX;
×
UNCOV
162
    strlcpy(addr.sun_path, sockettarget, sizeof(addr.sun_path));
×
UNCOV
163
    addr.sun_path[sizeof(addr.sun_path) - 1] = 0;
×
UNCOV
164
    len = (socklen_t)(strlen(addr.sun_path) + sizeof(addr.sun_family) + 1);
×
165

166
    /* create socket */
UNCOV
167
    this->socket = socket(AF_UNIX, SOCK_STREAM, 0);
×
UNCOV
168
    if (this->socket == -1) {
×
169
        SCLogWarning(
×
170
                "Unix Socket: unable to create UNIX socket %s: %s", addr.sun_path, strerror(errno));
×
171
        return 0;
×
172
    }
×
UNCOV
173
    this->select_max = this->socket + 1;
×
174

175
    /* set reuse option */
UNCOV
176
    ret = setsockopt(this->socket, SOL_SOCKET, SO_REUSEADDR,
×
UNCOV
177
                     (char *) &on, sizeof(on));
×
UNCOV
178
    if ( ret != 0 ) {
×
179
        SCLogWarning("Cannot set sockets options: %s.", strerror(errno));
×
180
    }
×
181

182
    /* bind socket */
UNCOV
183
    ret = bind(this->socket, (struct sockaddr *) &addr, len);
×
UNCOV
184
    if (ret == -1) {
×
185
        SCLogWarning("Unix socket: UNIX socket bind(%s) error: %s", sockettarget, strerror(errno));
×
186
        return 0;
×
187
    }
×
188

UNCOV
189
#if !(defined OS_FREEBSD || defined __OpenBSD__)
×
190
    /* Set file mode: will not fully work on most system, the group
191
     * permission is not changed on some Linux. *BSD won't do the
192
     * chmod: it returns EINVAL when calling chmod on sockets. */
UNCOV
193
    ret = chmod(sockettarget, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP);
×
UNCOV
194
    if (ret == -1) {
×
195
        int err = errno;
×
196
        SCLogWarning("Unable to change permission on socket: %s (%d)", strerror(err), err);
×
197
    }
×
UNCOV
198
#endif
×
199

200
    /* listen */
UNCOV
201
    if (listen(this->socket, 1) == -1) {
×
202
        SCLogWarning("Command server: UNIX socket listen() error: %s", strerror(errno));
×
203
        return 0;
×
204
    }
×
UNCOV
205
    return 1;
×
UNCOV
206
}
×
207

208
static void UnixCommandSetMaxFD(UnixCommand *this)
UNCOV
209
{
×
UNCOV
210
    UnixClient *item;
×
211

UNCOV
212
    if (this == NULL) {
×
213
        SCLogError("Unix command is NULL, warn devel");
×
214
        return;
×
215
    }
×
216

UNCOV
217
    this->select_max = this->socket + 1;
×
UNCOV
218
    TAILQ_FOREACH(item, &this->clients, next) {
×
UNCOV
219
        if (item->fd >= this->select_max) {
×
UNCOV
220
            this->select_max = item->fd + 1;
×
UNCOV
221
        }
×
UNCOV
222
    }
×
UNCOV
223
}
×
224

225
static UnixClient *UnixClientAlloc(void)
UNCOV
226
{
×
UNCOV
227
    UnixClient *uclient = SCMalloc(sizeof(UnixClient));
×
UNCOV
228
    if (unlikely(uclient == NULL)) {
×
229
        SCLogError("Can't allocate new client");
×
230
        return NULL;
×
231
    }
×
UNCOV
232
    uclient->mbuf = MemBufferCreateNew(CLIENT_BUFFER_SIZE);
×
UNCOV
233
    if (uclient->mbuf == NULL) {
×
234
        SCLogError("Can't allocate new client send buffer");
×
235
        SCFree(uclient);
×
236
        return NULL;
×
237
    }
×
UNCOV
238
    return uclient;
×
UNCOV
239
}
×
240

241
static void UnixClientFree(UnixClient *c)
UNCOV
242
{
×
UNCOV
243
    if (c != NULL) {
×
UNCOV
244
        MemBufferFree(c->mbuf);
×
UNCOV
245
        SCFree(c);
×
UNCOV
246
    }
×
UNCOV
247
}
×
248

249
/**
250
 * \brief Close the unix socket
251
 */
252
static void UnixCommandClose(UnixCommand  *this, int fd)
UNCOV
253
{
×
UNCOV
254
    UnixClient *item;
×
UNCOV
255
    UnixClient *safe = NULL;
×
UNCOV
256
    int found = 0;
×
257

UNCOV
258
    TAILQ_FOREACH_SAFE (item, &this->clients, next, safe) {
×
UNCOV
259
        if (item->fd == fd) {
×
UNCOV
260
            found = 1;
×
UNCOV
261
            break;
×
UNCOV
262
        }
×
UNCOV
263
    }
×
264

UNCOV
265
    if (found == 0) {
×
266
        SCLogError("No fd found in client list");
×
267
        return;
×
268
    }
×
269

UNCOV
270
    TAILQ_REMOVE(&this->clients, item, next);
×
271

UNCOV
272
    close(item->fd);
×
UNCOV
273
    UnixCommandSetMaxFD(this);
×
UNCOV
274
    UnixClientFree(item);
×
UNCOV
275
}
×
276

277
#define UNIX_PROTO_VERSION_LENGTH 200
UNCOV
278
#define UNIX_PROTO_VERSION_V1 "0.1"
×
UNCOV
279
#define UNIX_PROTO_V1 1
×
UNCOV
280
#define UNIX_PROTO_VERSION "0.2"
×
UNCOV
281
#define UNIX_PROTO_V2 2
×
282

283
static int UnixCommandSendJSONToClient(UnixClient *client, json_t *js)
UNCOV
284
{
×
UNCOV
285
    MemBufferReset(client->mbuf);
×
286

UNCOV
287
    OutputJSONMemBufferWrapper wrapper = {
×
UNCOV
288
        .buffer = &client->mbuf,
×
UNCOV
289
        .expand_by = CLIENT_BUFFER_SIZE
×
UNCOV
290
    };
×
291

UNCOV
292
    int r = json_dump_callback(js, OutputJSONMemBufferCallback, &wrapper,
×
UNCOV
293
            JSON_PRESERVE_ORDER|JSON_COMPACT|JSON_ENSURE_ASCII|
×
UNCOV
294
            JSON_ESCAPE_SLASH);
×
UNCOV
295
    if (r != 0) {
×
296
        SCLogWarning("unable to serialize JSON object");
×
297
        return -1;
×
298
    }
×
299

UNCOV
300
    if (client->version > UNIX_PROTO_V1) {
×
UNCOV
301
        if (MEMBUFFER_OFFSET(client->mbuf) + 1 >= MEMBUFFER_SIZE(client->mbuf)) {
×
302
            MemBufferExpand(&client->mbuf, 1);
×
303
        }
×
UNCOV
304
        MemBufferWriteString(client->mbuf, "\n");
×
UNCOV
305
    }
×
306

UNCOV
307
    if (send(client->fd, (const char *)MEMBUFFER_BUFFER(client->mbuf),
×
UNCOV
308
                MEMBUFFER_OFFSET(client->mbuf), MSG_NOSIGNAL) == -1)
×
309
    {
×
310
        SCLogWarning("unable to send block of size "
×
311
                     "%" PRIuMAX ": %s",
×
312
                (uintmax_t)MEMBUFFER_OFFSET(client->mbuf), strerror(errno));
×
313
        return -1;
×
314
    }
×
315

UNCOV
316
    SCLogDebug("sent message of size %"PRIuMAX" to client socket %d",
×
UNCOV
317
            (uintmax_t)MEMBUFFER_OFFSET(client->mbuf), client->fd);
×
UNCOV
318
    return 0;
×
UNCOV
319
}
×
320

321
/**
322
 * \brief Accept a new client on unix socket
323
 *
324
 *  The function is called when a new user is detected
325
 *  in UnixMain(). It does the initial protocol negotiation
326
 *  with client.
327
 *
328
 * \retval 0 in case of error, 1 in case of success
329
 */
330
static int UnixCommandAccept(UnixCommand *this)
UNCOV
331
{
×
UNCOV
332
    char buffer[UNIX_PROTO_VERSION_LENGTH + 1];
×
UNCOV
333
    json_t *client_msg;
×
UNCOV
334
    json_t *server_msg;
×
UNCOV
335
    json_t *version;
×
UNCOV
336
    json_error_t jerror;
×
UNCOV
337
    int client;
×
UNCOV
338
    int client_version;
×
UNCOV
339
    ssize_t ret;
×
UNCOV
340
    UnixClient *uclient = NULL;
×
341

342
    /* accept client socket */
UNCOV
343
    socklen_t len = sizeof(this->client_addr);
×
UNCOV
344
    client = accept(this->socket, (struct sockaddr *) &this->client_addr,
×
UNCOV
345
                          &len);
×
UNCOV
346
    if (client < 0) {
×
347
        SCLogInfo("Unix socket: accept() error: %s",
×
348
                  strerror(errno));
×
349
        return 0;
×
350
    }
×
UNCOV
351
    SCLogDebug("Unix socket: client connection");
×
352

353
    /* read client version */
UNCOV
354
    buffer[sizeof(buffer)-1] = 0;
×
UNCOV
355
    ret = recv(client, buffer, sizeof(buffer)-1, 0);
×
UNCOV
356
    if (ret < 0) {
×
357
        SCLogInfo("Command server: client doesn't send version");
×
358
        close(client);
×
359
        return 0;
×
360
    }
×
UNCOV
361
    if (ret >= (int)(sizeof(buffer)-1)) {
×
362
        SCLogInfo("Command server: client message is too long, "
×
363
                  "disconnect him.");
×
364
        close(client);
×
365
        return 0;
×
366
    }
×
UNCOV
367
    buffer[ret] = 0;
×
368

UNCOV
369
    client_msg = json_loads(buffer, 0, &jerror);
×
UNCOV
370
    if (client_msg == NULL) {
×
371
        SCLogInfo("Invalid command, error on line %d: %s\n", jerror.line, jerror.text);
×
372
        close(client);
×
373
        return 0;
×
374
    }
×
375

UNCOV
376
    version = json_object_get(client_msg, "version");
×
UNCOV
377
    if (!json_is_string(version)) {
×
378
        SCLogInfo("error: version is not a string");
×
379
        close(client);
×
380
        json_decref(client_msg);
×
381
        return 0;
×
382
    }
×
383

384
    /* check client version */
UNCOV
385
    if ((strcmp(json_string_value(version), UNIX_PROTO_VERSION) != 0)
×
UNCOV
386
        && (strcmp(json_string_value(version), UNIX_PROTO_VERSION_V1) != 0)) {
×
387
        SCLogInfo("Unix socket: invalid client version: \"%s\"",
×
388
                json_string_value(version));
×
389
        json_decref(client_msg);
×
390
        close(client);
×
391
        return 0;
×
UNCOV
392
    } else {
×
UNCOV
393
        SCLogDebug("Unix socket: client version: \"%s\"",
×
UNCOV
394
                json_string_value(version));
×
UNCOV
395
        if (strcmp(json_string_value(version), UNIX_PROTO_VERSION_V1) == 0) {
×
396
            client_version = UNIX_PROTO_V1;
×
UNCOV
397
        } else {
×
UNCOV
398
            client_version = UNIX_PROTO_V2;
×
UNCOV
399
        }
×
UNCOV
400
    }
×
401

UNCOV
402
    json_decref(client_msg);
×
403
    /* send answer */
UNCOV
404
    server_msg = json_object();
×
UNCOV
405
    if (server_msg == NULL) {
×
406
        close(client);
×
407
        return 0;
×
408
    }
×
UNCOV
409
    json_object_set_new(server_msg, "return", json_string("OK"));
×
410

UNCOV
411
    uclient = UnixClientAlloc();
×
UNCOV
412
    if (unlikely(uclient == NULL)) {
×
413
        json_decref(server_msg);
×
414
        close(client);
×
415
        return 0;
×
416
    }
×
UNCOV
417
    uclient->fd = client;
×
UNCOV
418
    uclient->version = client_version;
×
419

UNCOV
420
    if (UnixCommandSendJSONToClient(uclient, server_msg) != 0) {
×
421
        SCLogWarning("Unable to send command");
×
422

423
        UnixClientFree(uclient);
×
424
        json_decref(server_msg);
×
425
        close(client);
×
426
        return 0;
×
427
    }
×
428

UNCOV
429
    json_decref(server_msg);
×
430

431
    /* client connected */
UNCOV
432
    SCLogDebug("Unix socket: client connected");
×
UNCOV
433
    TAILQ_INSERT_TAIL(&this->clients, uclient, next);
×
UNCOV
434
    UnixCommandSetMaxFD(this);
×
UNCOV
435
    return 1;
×
UNCOV
436
}
×
437

438
static int UnixCommandBackgroundTasks(UnixCommand* this)
UNCOV
439
{
×
UNCOV
440
    int ret = 1;
×
UNCOV
441
    Task *ltask;
×
442

UNCOV
443
    TAILQ_FOREACH(ltask, &this->tasks, next) {
×
UNCOV
444
        int fret = ltask->Func(ltask->data);
×
UNCOV
445
        if (fret != TM_ECODE_OK) {
×
446
            ret = 0;
×
447
        }
×
UNCOV
448
    }
×
UNCOV
449
    return ret;
×
UNCOV
450
}
×
451

452
/**
453
 * \brief Command dispatcher
454
 *
455
 * \param this a UnixCommand:: structure
456
 * \param command a string containing a json formatted
457
 * command
458
 *
459
 * \retval 0 in case of error, 1 in case of success
460
 */
461
static int UnixCommandExecute(UnixCommand * this, char *command, UnixClient *client)
UNCOV
462
{
×
UNCOV
463
    int ret = 1;
×
UNCOV
464
    json_error_t error;
×
UNCOV
465
    json_t *jsoncmd = NULL;
×
UNCOV
466
    json_t *cmd = NULL;
×
UNCOV
467
    json_t *server_msg = json_object();
×
UNCOV
468
    const char * value;
×
UNCOV
469
    int found = 0;
×
UNCOV
470
    Command *lcmd;
×
471

UNCOV
472
    if (server_msg == NULL) {
×
473
        return 0;
×
474
    }
×
475

UNCOV
476
    jsoncmd = json_loads(command, 0, &error);
×
UNCOV
477
    if (jsoncmd == NULL) {
×
478
        SCLogInfo("Invalid command, error on line %d: %s\n", error.line, error.text);
×
479
        goto error;
×
480
    }
×
481

UNCOV
482
    cmd = json_object_get(jsoncmd, "command");
×
UNCOV
483
    if(!json_is_string(cmd)) {
×
484
        SCLogInfo("error: command is not a string");
×
485
        goto error_cmd;
×
486
    }
×
UNCOV
487
    value = json_string_value(cmd);
×
488

UNCOV
489
    TAILQ_FOREACH(lcmd, &this->commands, next) {
×
UNCOV
490
        if (!strcmp(value, lcmd->name)) {
×
UNCOV
491
            int fret = TM_ECODE_OK;
×
UNCOV
492
            found = 1;
×
UNCOV
493
            if (lcmd->flags & UNIX_CMD_TAKE_ARGS) {
×
UNCOV
494
                cmd = json_object_get(jsoncmd, "arguments");
×
UNCOV
495
                if(!json_is_object(cmd)) {
×
496
                    SCLogInfo("error: argument is not an object");
×
497
                    goto error_cmd;
×
498
                }
×
UNCOV
499
            }
×
UNCOV
500
            fret = lcmd->Func(cmd, server_msg, lcmd->data);
×
UNCOV
501
            if (fret != TM_ECODE_OK) {
×
UNCOV
502
                ret = 0;
×
UNCOV
503
            }
×
UNCOV
504
            break;
×
UNCOV
505
        }
×
UNCOV
506
    }
×
507

UNCOV
508
    if (found == 0) {
×
509
        json_object_set_new(server_msg, "message", json_string("Unknown command"));
×
510
        ret = 0;
×
511
    }
×
512

UNCOV
513
    switch (ret) {
×
UNCOV
514
        case 0:
×
UNCOV
515
            json_object_set_new(server_msg, "return", json_string("NOK"));
×
UNCOV
516
            break;
×
UNCOV
517
        case 1:
×
UNCOV
518
            json_object_set_new(server_msg, "return", json_string("OK"));
×
UNCOV
519
            break;
×
UNCOV
520
    }
×
521

UNCOV
522
    if (UnixCommandSendJSONToClient(client, server_msg) != 0) {
×
523
        goto error_cmd;
×
524
    }
×
525

UNCOV
526
    json_decref(jsoncmd);
×
UNCOV
527
    json_decref(server_msg);
×
UNCOV
528
    return ret;
×
529

530
error_cmd:
×
531
    json_decref(jsoncmd);
×
532
error:
×
533
    json_decref(server_msg);
×
534
    UnixCommandClose(this, client->fd);
×
535
    return 0;
×
536
}
×
537

538
static void UnixCommandRun(UnixCommand * this, UnixClient *client)
UNCOV
539
{
×
UNCOV
540
    char buffer[4096];
×
UNCOV
541
    ssize_t ret;
×
UNCOV
542
    if (client->version <= UNIX_PROTO_V1) {
×
543
        ret = recv(client->fd, buffer, sizeof(buffer) - 1, 0);
×
544
        if (ret <= 0) {
×
545
            if (ret == 0) {
×
546
                SCLogDebug("Unix socket: lost connection with client");
×
547
            } else {
×
548
                SCLogError("Unix socket: error on recv() from client: %s", strerror(errno));
×
549
            }
×
550
            UnixCommandClose(this, client->fd);
×
551
            return;
×
552
        }
×
553
        if (ret >= (int)(sizeof(buffer)-1)) {
×
554
            SCLogError("Command server: client command is too long, "
×
555
                       "disconnect him.");
×
556
            UnixCommandClose(this, client->fd);
×
557
            return;
×
558
        }
×
559
        buffer[ret] = 0;
×
UNCOV
560
    } else {
×
UNCOV
561
        int try = 0;
×
UNCOV
562
        int offset = 0;
×
UNCOV
563
        int cmd_over = 0;
×
UNCOV
564
        ret = recv(client->fd, buffer + offset, sizeof(buffer) - offset - 1, 0);
×
UNCOV
565
        do {
×
UNCOV
566
            if (ret <= 0) {
×
UNCOV
567
                if (ret == 0) {
×
UNCOV
568
                    SCLogDebug("Unix socket: lost connection with client");
×
UNCOV
569
                } else {
×
570
                    SCLogError("Unix socket: error on recv() from client: %s", strerror(errno));
×
571
                }
×
UNCOV
572
                UnixCommandClose(this, client->fd);
×
UNCOV
573
                return;
×
UNCOV
574
            }
×
UNCOV
575
            if (ret >= (int)(sizeof(buffer)- offset - 1)) {
×
576
                SCLogInfo("Command server: client command is too long, "
×
577
                        "disconnect him.");
×
578
                UnixCommandClose(this, client->fd);
×
579
                return;
×
580
            }
×
UNCOV
581
            if (buffer[ret - 1] == '\n') {
×
UNCOV
582
                buffer[ret-1] = 0;
×
UNCOV
583
                cmd_over = 1;
×
UNCOV
584
            } else {
×
585
                struct timeval tv;
×
586
                fd_set select_set;
×
587
                offset += ret;
×
588
                do {
×
589
                    FD_ZERO(&select_set);
×
590
                    FD_SET(client->fd, &select_set);
×
591
                    tv.tv_sec = 0;
×
592
                    tv.tv_usec = 200 * 1000;
×
593
                    try++;
×
594
                    ret = select(client->fd, &select_set, NULL, NULL, &tv);
×
595
                    /* catch select() error */
596
                    if (ret == -1) {
×
597
                        /* Signal was caught: just ignore it */
598
                        if (errno != EINTR) {
×
599
                            SCLogInfo("Unix socket: lost connection with client");
×
600
                            UnixCommandClose(this, client->fd);
×
601
                            return;
×
602
                        }
×
603
                    }
×
604
                } while (ret == 0 && try < 3);
×
605
                if (ret > 0) {
×
606
                    ret = recv(client->fd, buffer + offset,
×
607
                               sizeof(buffer) - offset - 1, 0);
×
608
                }
×
609
            }
×
UNCOV
610
        } while (try < 3 && cmd_over == 0);
×
611

UNCOV
612
        if (try == 3 && cmd_over == 0) {
×
613
            SCLogInfo("Unix socket: incomplete client message, closing connection");
×
614
            UnixCommandClose(this, client->fd);
×
615
            return;
×
616
        }
×
UNCOV
617
    }
×
UNCOV
618
    UnixCommandExecute(this, buffer, client);
×
UNCOV
619
}
×
620

621
/**
622
 * \brief Select function
623
 *
624
 * \retval 0 in case of error, 1 in case of success
625
 */
626
static int UnixMain(UnixCommand * this)
UNCOV
627
{
×
UNCOV
628
    struct timeval tv;
×
UNCOV
629
    int ret;
×
UNCOV
630
    fd_set select_set;
×
UNCOV
631
    UnixClient *uclient;
×
UNCOV
632
    UnixClient *tclient;
×
633

UNCOV
634
    if (suricata_ctl_flags & SURICATA_STOP) {
×
UNCOV
635
        TAILQ_FOREACH_SAFE (uclient, &this->clients, next, tclient) {
×
UNCOV
636
            UnixCommandClose(this, uclient->fd);
×
UNCOV
637
        }
×
UNCOV
638
        return 1;
×
UNCOV
639
    }
×
640

641
    /* Wait activity on the socket */
UNCOV
642
    FD_ZERO(&select_set);
×
UNCOV
643
    FD_SET(this->socket, &select_set);
×
UNCOV
644
    TAILQ_FOREACH(uclient, &this->clients, next) {
×
UNCOV
645
        FD_SET(uclient->fd, &select_set);
×
UNCOV
646
    }
×
647

UNCOV
648
    tv.tv_sec = 0;
×
UNCOV
649
    tv.tv_usec = 200 * 1000;
×
UNCOV
650
    ret = select(this->select_max, &select_set, NULL, NULL, &tv);
×
651

652
    /* catch select() error */
UNCOV
653
    if (ret == -1) {
×
654
        /* Signal was caught: just ignore it */
655
        if (errno == EINTR) {
×
656
            return 1;
×
657
        }
×
658
        SCLogError("Command server: select() fatal error: %s", strerror(errno));
×
659
        return 0;
×
660
    }
×
661

662
    /* timeout: continue */
UNCOV
663
    if (ret == 0) {
×
UNCOV
664
        return 1;
×
UNCOV
665
    }
×
666

UNCOV
667
    TAILQ_FOREACH_SAFE(uclient, &this->clients, next, tclient) {
×
UNCOV
668
        if (FD_ISSET(uclient->fd, &select_set)) {
×
UNCOV
669
            UnixCommandRun(this, uclient);
×
UNCOV
670
        }
×
UNCOV
671
    }
×
UNCOV
672
    if (FD_ISSET(this->socket, &select_set)) {
×
UNCOV
673
        if (!UnixCommandAccept(this))
×
674
            return 1;
×
UNCOV
675
    }
×
676

UNCOV
677
    return 1;
×
UNCOV
678
}
×
679

680
static TmEcode UnixManagerShutdownCommand(json_t *cmd,
681
                                   json_t *server_msg, void *data)
UNCOV
682
{
×
UNCOV
683
    SCEnter();
×
UNCOV
684
    json_object_set_new(server_msg, "message", json_string("Closing Suricata"));
×
UNCOV
685
    EngineStop();
×
UNCOV
686
    SCReturnInt(TM_ECODE_OK);
×
UNCOV
687
}
×
688

689
static TmEcode UnixManagerVersionCommand(json_t *cmd,
690
                                   json_t *server_msg, void *data)
UNCOV
691
{
×
UNCOV
692
    SCEnter();
×
UNCOV
693
    json_object_set_new(server_msg, "message", json_string(GetProgramVersion()));
×
UNCOV
694
    SCReturnInt(TM_ECODE_OK);
×
UNCOV
695
}
×
696

697
static TmEcode UnixManagerUptimeCommand(json_t *cmd,
698
                                 json_t *server_msg, void *data)
UNCOV
699
{
×
UNCOV
700
    SCEnter();
×
UNCOV
701
    time_t uptime;
×
UNCOV
702
    UnixCommand *ucmd = (UnixCommand *)data;
×
703

UNCOV
704
    uptime = time(NULL) - ucmd->start_timestamp;
×
UNCOV
705
    json_object_set_new(server_msg, "message", json_integer(uptime));
×
UNCOV
706
    SCReturnInt(TM_ECODE_OK);
×
UNCOV
707
}
×
708

709
static TmEcode UnixManagerRunningModeCommand(json_t *cmd,
710
                                      json_t *server_msg, void *data)
UNCOV
711
{
×
UNCOV
712
    SCEnter();
×
UNCOV
713
    json_object_set_new(server_msg, "message", json_string(RunmodeGetActive()));
×
UNCOV
714
    SCReturnInt(TM_ECODE_OK);
×
UNCOV
715
}
×
716

717
static TmEcode UnixManagerCaptureModeCommand(json_t *cmd,
718
                                      json_t *server_msg, void *data)
UNCOV
719
{
×
UNCOV
720
    SCEnter();
×
UNCOV
721
    json_object_set_new(server_msg, "message", json_string(RunModeGetMainMode()));
×
UNCOV
722
    SCReturnInt(TM_ECODE_OK);
×
UNCOV
723
}
×
724

725
static TmEcode UnixManagerReloadRulesWrapper(json_t *cmd, json_t *server_msg, void *data, int do_wait)
UNCOV
726
{
×
UNCOV
727
    SCEnter();
×
728

UNCOV
729
    if (SuriHasSigFile()) {
×
730
        json_object_set_new(server_msg, "message",
×
731
                            json_string("Live rule reload not possible if -s "
×
732
                                        "or -S option used at runtime."));
×
733
        SCReturnInt(TM_ECODE_FAILED);
×
734
    }
×
735

UNCOV
736
    int r = DetectEngineReloadStart();
×
737

UNCOV
738
    if (r == 0 && do_wait) {
×
UNCOV
739
        while (!DetectEngineReloadIsIdle())
×
UNCOV
740
            usleep(100);
×
UNCOV
741
    } else {
×
742
        if (r == -1) {
×
743
            json_object_set_new(server_msg, "message", json_string("Reload already in progress"));
×
744
            SCReturnInt(TM_ECODE_FAILED);
×
745
        }
×
746
    }
×
747

UNCOV
748
    json_object_set_new(server_msg, "message", json_string("done"));
×
UNCOV
749
    SCReturnInt(TM_ECODE_OK);
×
UNCOV
750
}
×
751

752
static TmEcode UnixManagerReloadRules(json_t *cmd, json_t *server_msg, void *data)
UNCOV
753
{
×
UNCOV
754
    return UnixManagerReloadRulesWrapper(cmd, server_msg, data, 1);
×
UNCOV
755
}
×
756

757
static TmEcode UnixManagerNonBlockingReloadRules(json_t *cmd, json_t *server_msg, void *data)
758
{
×
759
    return UnixManagerReloadRulesWrapper(cmd, server_msg, data, 0);
×
760
}
×
761

762
static TmEcode UnixManagerReloadTimeCommand(json_t *cmd,
763
                                            json_t *server_msg, void *data)
764
{
×
765
    SCEnter();
×
766
    TmEcode retval;
×
767
    json_t *jdata = NULL;
×
768

769
    retval = OutputEngineStatsReloadTime(&jdata);
×
770
    json_object_set_new(server_msg, "message", jdata);
×
771
    SCReturnInt(retval);
×
772
}
×
773

774
static TmEcode UnixManagerRulesetStatsCommand(json_t *cmd,
775
                                              json_t *server_msg, void *data)
776
{
×
777
    SCEnter();
×
778
    TmEcode retval;
×
779
    json_t *jdata = NULL;
×
780

781
    retval = OutputEngineStatsRuleset(&jdata);
×
782
    json_object_set_new(server_msg, "message", jdata);
×
783
    SCReturnInt(retval);
×
784
}
×
785

786
#ifdef PROFILE_RULES
787
static TmEcode UnixManagerRulesetProfileCommand(json_t *cmd, json_t *server_msg, void *data)
788
{
789
    SCEnter();
790
    DetectEngineCtx *de_ctx = DetectEngineGetCurrent();
791

792
    json_t *js = SCProfileRuleTriggerDump(de_ctx);
793
    if (js == NULL) {
794
        json_object_set_new(server_msg, "message", json_string("NOK"));
795
        SCReturnInt(TM_ECODE_FAILED);
796
    }
797
    json_object_set_new(server_msg, "message", js);
798
    SCReturnInt(TM_ECODE_OK);
799
}
800

801
static TmEcode UnixManagerRulesetProfileStartCommand(json_t *cmd, json_t *server_msg, void *data)
802
{
803
    SCEnter();
804
    SCProfileRuleStartCollection();
805
    json_object_set_new(server_msg, "message", json_string("OK"));
806
    SCReturnInt(TM_ECODE_OK);
807
}
808

809
static TmEcode UnixManagerRulesetProfileStopCommand(json_t *cmd, json_t *server_msg, void *data)
810
{
811
    SCEnter();
812
    SCProfileRuleStopCollection();
813
    json_object_set_new(server_msg, "message", json_string("OK"));
814
    SCReturnInt(TM_ECODE_OK);
815
}
816
#endif
817

818
static TmEcode UnixManagerShowFailedRules(json_t *cmd,
819
                                          json_t *server_msg, void *data)
820
{
×
821
    SCEnter();
×
822
    int rules_cnt = 0;
×
823
    DetectEngineCtx *de_ctx = DetectEngineGetCurrent();
×
824
    if (de_ctx == NULL) {
×
825
        json_object_set_new(server_msg, "message", json_string("Unable to get info"));
×
826
        SCReturnInt(TM_ECODE_OK);
×
827
    }
×
828

829
    /* Since we need to deference de_ctx, we don't want to lost it. */
830
    DetectEngineCtx *list = de_ctx;
×
831
    json_t *js_sigs_array = json_array();
×
832

833
    if (js_sigs_array == NULL) {
×
834
        json_object_set_new(server_msg, "message", json_string("Unable to get info"));
×
835
        goto error;
×
836
    }
×
837
    while (list) {
×
838
        SigString *sigs_str = NULL;
×
839
        TAILQ_FOREACH(sigs_str, &list->sig_stat.failed_sigs, next) {
×
840
            json_t *jdata = json_object();
×
841
            if (jdata == NULL) {
×
842
                json_object_set_new(server_msg, "message", json_string("Unable to get the sig"));
×
843
                goto error;
×
844
            }
×
845

846
            json_object_set_new(jdata, "tenant_id", json_integer(list->tenant_id));
×
847
            json_object_set_new(jdata, "rule", json_string(sigs_str->sig_str));
×
848
            json_object_set_new(jdata, "filename", json_string(sigs_str->filename));
×
849
            json_object_set_new(jdata, "line", json_integer(sigs_str->line));
×
850
            if (sigs_str->sig_error) {
×
851
                json_object_set_new(jdata, "error", json_string(sigs_str->sig_error));
×
852
            }
×
853
            json_array_append_new(js_sigs_array, jdata);
×
854
            if (++rules_cnt > MAX_FAILED_RULES) {
×
855
                break;
×
856
            }
×
857
        }
×
858
        if (rules_cnt > MAX_FAILED_RULES) {
×
859
            break;
×
860
        }
×
861
        list = list->next;
×
862
    }
×
863

864
    json_object_set_new(server_msg, "message", js_sigs_array);
×
865
    DetectEngineDeReference(&de_ctx);
×
866
    SCReturnInt(TM_ECODE_OK);
×
867

868
error:
×
869
    DetectEngineDeReference(&de_ctx);
×
870
    json_object_clear(js_sigs_array);
×
871
    json_decref(js_sigs_array);
×
872
    SCReturnInt(TM_ECODE_FAILED);
×
873
}
×
874

875
static TmEcode UnixManagerConfGetCommand(json_t *cmd,
876
                                         json_t *server_msg, void *data)
877
{
×
878
    SCEnter();
×
879

880
    const char *confval = NULL;
×
881
    char *variable = NULL;
×
882

883
    json_t *jarg = json_object_get(cmd, "variable");
×
884
    if(!json_is_string(jarg)) {
×
885
        SCLogInfo("error: variable is not a string");
×
886
        json_object_set_new(server_msg, "message", json_string("variable is not a string"));
×
887
        SCReturnInt(TM_ECODE_FAILED);
×
888
    }
×
889

890
    variable = (char *)json_string_value(jarg);
×
891
    if (SCConfGet(variable, &confval) != 1) {
×
892
        json_object_set_new(server_msg, "message", json_string("Unable to get value"));
×
893
        SCReturnInt(TM_ECODE_FAILED);
×
894
    }
×
895

896
    if (confval) {
×
897
        json_object_set_new(server_msg, "message", json_string(confval));
×
898
        SCReturnInt(TM_ECODE_OK);
×
899
    }
×
900

901
    json_object_set_new(server_msg, "message", json_string("No string value"));
×
902
    SCReturnInt(TM_ECODE_FAILED);
×
903
}
×
904

905
static TmEcode UnixManagerListCommand(json_t *cmd,
906
                               json_t *answer, void *data)
UNCOV
907
{
×
UNCOV
908
    SCEnter();
×
UNCOV
909
    json_t *jdata;
×
UNCOV
910
    json_t *jarray;
×
UNCOV
911
    Command *lcmd = NULL;
×
UNCOV
912
    UnixCommand *gcmd = (UnixCommand *) data;
×
UNCOV
913
    int i = 0;
×
914

UNCOV
915
    jdata = json_object();
×
UNCOV
916
    if (jdata == NULL) {
×
917
        json_object_set_new(answer, "message",
×
918
                            json_string("internal error at json object creation"));
×
919
        return TM_ECODE_FAILED;
×
920
    }
×
UNCOV
921
    jarray = json_array();
×
UNCOV
922
    if (jarray == NULL) {
×
923
        json_object_set_new(answer, "message",
×
924
                            json_string("internal error at json object creation"));
×
925
        return TM_ECODE_FAILED;
×
926
    }
×
927

UNCOV
928
    TAILQ_FOREACH(lcmd, &gcmd->commands, next) {
×
UNCOV
929
        json_array_append_new(jarray, json_string(lcmd->name));
×
UNCOV
930
        i++;
×
UNCOV
931
    }
×
932

UNCOV
933
    json_object_set_new(jdata, "count", json_integer(i));
×
UNCOV
934
    json_object_set_new(jdata, "commands", jarray);
×
UNCOV
935
    json_object_set_new(answer, "message", jdata);
×
UNCOV
936
    SCReturnInt(TM_ECODE_OK);
×
UNCOV
937
}
×
938

939
static TmEcode UnixManagerReopenLogFiles(json_t *cmd, json_t *server_msg, void *data)
940
{
×
941
    OutputNotifyFileRotation();
×
942
    json_object_set_new(server_msg, "message", json_string("done"));
×
943
    SCReturnInt(TM_ECODE_OK);
×
944
}
×
945

946
#if 0
947
TmEcode UnixManagerReloadRules(json_t *cmd,
948
                               json_t *server_msg, void *data)
949
{
950
    SCEnter();
951
    if (suricata_ctl_flags != 0) {
952
        json_object_set_new(server_msg, "message",
953
                            json_string("Live rule swap no longer possible."
954
                                        " Engine in shutdown mode."));
955
        SCReturn(TM_ECODE_FAILED);
956
    } else {
957
        /* FIXME : need to check option value */
958
        UtilSignalHandlerSetup(SIGUSR2, SignalHandlerSigusr2Idle);
959
        DetectEngineSpawnLiveRuleSwapMgmtThread();
960
        json_object_set_new(server_msg, "message", json_string("Reloading rules"));
961
    }
962
    SCReturn(TM_ECODE_OK);
963
}
964
#endif
965

966
static UnixCommand command;
967

968
/**
969
 * \brief Add a command to the list of commands
970
 *
971
 * This function adds a command to the list of commands available
972
 * through the unix socket.
973
 * 
974
 * When a command is received from user through the unix socket, the content
975
 * of 'Command' field in the JSON message is match against keyword, then the
976
 * Func is called. See UnixSocketAddPcapFile() for an example.
977
 *
978
 * \param keyword name of the command
979
 * \param Func function to run when command is received
980
 * \param data a pointer to data that are passed to Func when it is run
981
 * \param flags a flag now used to tune the command type
982
 * \retval TM_ECODE_OK in case of success, TM_ECODE_FAILED in case of failure
983
 */
984
TmEcode UnixManagerRegisterCommand(const char * keyword,
985
                                   TmEcode (*Func)(json_t *, json_t *, void *),
986
                                   void *data, int flags)
UNCOV
987
{
×
UNCOV
988
    SCEnter();
×
UNCOV
989
    Command *cmd = NULL;
×
UNCOV
990
    Command *lcmd = NULL;
×
991

UNCOV
992
    if (Func == NULL) {
×
993
        SCLogError("Null function");
×
994
        SCReturnInt(TM_ECODE_FAILED);
×
995
    }
×
996

UNCOV
997
    if (keyword == NULL) {
×
998
        SCLogError("Null keyword");
×
999
        SCReturnInt(TM_ECODE_FAILED);
×
1000
    }
×
1001

UNCOV
1002
    TAILQ_FOREACH(lcmd, &command.commands, next) {
×
UNCOV
1003
        if (!strcmp(keyword, lcmd->name)) {
×
1004
            SCLogError("%s already registered", keyword);
×
1005
            SCReturnInt(TM_ECODE_FAILED);
×
1006
        }
×
UNCOV
1007
    }
×
1008

UNCOV
1009
    cmd = SCMalloc(sizeof(Command));
×
UNCOV
1010
    if (unlikely(cmd == NULL)) {
×
1011
        SCLogError("Can't alloc cmd");
×
1012
        SCReturnInt(TM_ECODE_FAILED);
×
1013
    }
×
UNCOV
1014
    cmd->name = SCStrdup(keyword);
×
UNCOV
1015
    if (unlikely(cmd->name == NULL)) {
×
1016
        SCLogError("Can't alloc cmd name");
×
1017
        SCFree(cmd);
×
1018
        SCReturnInt(TM_ECODE_FAILED);
×
1019
    }
×
UNCOV
1020
    cmd->Func = Func;
×
UNCOV
1021
    cmd->data = data;
×
UNCOV
1022
    cmd->flags = flags;
×
1023
    /* Add it to the list */
UNCOV
1024
    TAILQ_INSERT_TAIL(&command.commands, cmd, next);
×
1025

UNCOV
1026
    SCReturnInt(TM_ECODE_OK);
×
UNCOV
1027
}
×
1028

1029
/**
1030
 * \brief Add a task to the list of tasks
1031
 *
1032
 * This function adds a task to run in the background. The task is run
1033
 * each time the UnixMain() function exits from select.
1034
 * 
1035
 * \param Func function to run when a command is received
1036
 * \param data a pointer to data that are passed to Func when it is run
1037
 * \retval TM_ECODE_OK in case of success, TM_ECODE_FAILED in case of failure
1038
 */
1039
TmEcode UnixManagerRegisterBackgroundTask(TmEcode (*Func)(void *),
1040
                                          void *data)
UNCOV
1041
{
×
UNCOV
1042
    SCEnter();
×
UNCOV
1043
    Task *task = NULL;
×
1044

UNCOV
1045
    if (Func == NULL) {
×
1046
        SCLogError("Null function");
×
1047
        SCReturnInt(TM_ECODE_FAILED);
×
1048
    }
×
1049

UNCOV
1050
    task = SCMalloc(sizeof(Task));
×
UNCOV
1051
    if (unlikely(task == NULL)) {
×
1052
        SCLogError("Can't alloc task");
×
1053
        SCReturnInt(TM_ECODE_FAILED);
×
1054
    }
×
UNCOV
1055
    task->Func = Func;
×
UNCOV
1056
    task->data = data;
×
1057
    /* Add it to the list */
UNCOV
1058
    TAILQ_INSERT_TAIL(&command.tasks, task, next);
×
1059

UNCOV
1060
    SCReturnInt(TM_ECODE_OK);
×
UNCOV
1061
}
×
1062

1063
int UnixManagerInit(void)
UNCOV
1064
{
×
UNCOV
1065
    if (UnixNew(&command) == 0) {
×
1066
        int failure_fatal = 0;
×
1067
        if (SCConfGetBool("engine.init-failure-fatal", &failure_fatal) != 1) {
×
1068
            SCLogDebug("ConfGetBool could not load the value.");
×
1069
        }
×
1070
        if (failure_fatal) {
×
1071
            FatalError("Unable to create unix command socket");
×
1072
        } else {
×
1073
            SCLogWarning("Unable to create unix command socket");
×
1074
            return -1;
×
1075
        }
×
1076
    }
×
1077

1078
    /* Init Unix socket */
UNCOV
1079
    UnixManagerRegisterCommand("shutdown", UnixManagerShutdownCommand, NULL, 0);
×
UNCOV
1080
    UnixManagerRegisterCommand("command-list", UnixManagerListCommand, &command, 0);
×
UNCOV
1081
    UnixManagerRegisterCommand("help", UnixManagerListCommand, &command, 0);
×
UNCOV
1082
    UnixManagerRegisterCommand("version", UnixManagerVersionCommand, &command, 0);
×
UNCOV
1083
    UnixManagerRegisterCommand("uptime", UnixManagerUptimeCommand, &command, 0);
×
UNCOV
1084
    UnixManagerRegisterCommand("running-mode", UnixManagerRunningModeCommand, &command, 0);
×
UNCOV
1085
    UnixManagerRegisterCommand("capture-mode", UnixManagerCaptureModeCommand, &command, 0);
×
UNCOV
1086
    UnixManagerRegisterCommand("conf-get", UnixManagerConfGetCommand, &command, UNIX_CMD_TAKE_ARGS);
×
UNCOV
1087
    UnixManagerRegisterCommand("dump-counters", StatsOutputCounterSocket, NULL, 0);
×
UNCOV
1088
    UnixManagerRegisterCommand("reload-rules", UnixManagerReloadRules, NULL, 0);
×
UNCOV
1089
    UnixManagerRegisterCommand("ruleset-reload-rules", UnixManagerReloadRules, NULL, 0);
×
UNCOV
1090
    UnixManagerRegisterCommand("ruleset-reload-nonblocking", UnixManagerNonBlockingReloadRules, NULL, 0);
×
UNCOV
1091
    UnixManagerRegisterCommand("ruleset-reload-time", UnixManagerReloadTimeCommand, NULL, 0);
×
UNCOV
1092
    UnixManagerRegisterCommand("ruleset-stats", UnixManagerRulesetStatsCommand, NULL, 0);
×
UNCOV
1093
    UnixManagerRegisterCommand("ruleset-failed-rules", UnixManagerShowFailedRules, NULL, 0);
×
1094
#ifdef PROFILE_RULES
1095
    UnixManagerRegisterCommand("ruleset-profile", UnixManagerRulesetProfileCommand, NULL, 0);
1096
    UnixManagerRegisterCommand(
1097
            "ruleset-profile-start", UnixManagerRulesetProfileStartCommand, NULL, 0);
1098
    UnixManagerRegisterCommand(
1099
            "ruleset-profile-stop", UnixManagerRulesetProfileStopCommand, NULL, 0);
1100
#endif
UNCOV
1101
    UnixManagerRegisterCommand("register-tenant-handler", UnixSocketRegisterTenantHandler, &command, UNIX_CMD_TAKE_ARGS);
×
UNCOV
1102
    UnixManagerRegisterCommand("unregister-tenant-handler", UnixSocketUnregisterTenantHandler, &command, UNIX_CMD_TAKE_ARGS);
×
UNCOV
1103
    UnixManagerRegisterCommand("register-tenant", UnixSocketRegisterTenant, &command, UNIX_CMD_TAKE_ARGS);
×
UNCOV
1104
    UnixManagerRegisterCommand("reload-tenant", UnixSocketReloadTenant, &command, UNIX_CMD_TAKE_ARGS);
×
UNCOV
1105
    UnixManagerRegisterCommand("reload-tenants", UnixSocketReloadTenants, &command, 0);
×
UNCOV
1106
    UnixManagerRegisterCommand("unregister-tenant", UnixSocketUnregisterTenant, &command, UNIX_CMD_TAKE_ARGS);
×
UNCOV
1107
    UnixManagerRegisterCommand("add-hostbit", UnixSocketHostbitAdd, &command, UNIX_CMD_TAKE_ARGS);
×
UNCOV
1108
    UnixManagerRegisterCommand("remove-hostbit", UnixSocketHostbitRemove, &command, UNIX_CMD_TAKE_ARGS);
×
UNCOV
1109
    UnixManagerRegisterCommand("list-hostbit", UnixSocketHostbitList, &command, UNIX_CMD_TAKE_ARGS);
×
UNCOV
1110
    UnixManagerRegisterCommand("reopen-log-files", UnixManagerReopenLogFiles, NULL, 0);
×
UNCOV
1111
    UnixManagerRegisterCommand("memcap-set", UnixSocketSetMemcap, &command, UNIX_CMD_TAKE_ARGS);
×
UNCOV
1112
    UnixManagerRegisterCommand("memcap-show", UnixSocketShowMemcap, &command, UNIX_CMD_TAKE_ARGS);
×
UNCOV
1113
    UnixManagerRegisterCommand("memcap-list", UnixSocketShowAllMemcap, NULL, 0);
×
1114

UNCOV
1115
    UnixManagerRegisterCommand("dataset-add", UnixSocketDatasetAdd, &command, UNIX_CMD_TAKE_ARGS);
×
UNCOV
1116
    UnixManagerRegisterCommand("dataset-remove", UnixSocketDatasetRemove, &command, UNIX_CMD_TAKE_ARGS);
×
UNCOV
1117
    UnixManagerRegisterCommand(
×
UNCOV
1118
            "dataset-add-json", UnixSocketDatajsonAdd, &command, UNIX_CMD_TAKE_ARGS);
×
UNCOV
1119
    UnixManagerRegisterCommand(
×
UNCOV
1120
            "get-flow-stats-by-id", UnixSocketGetFlowStatsById, &command, UNIX_CMD_TAKE_ARGS);
×
UNCOV
1121
    UnixManagerRegisterCommand("dataset-dump", UnixSocketDatasetDump, NULL, 0);
×
UNCOV
1122
    UnixManagerRegisterCommand(
×
UNCOV
1123
            "dataset-clear", UnixSocketDatasetClear, &command, UNIX_CMD_TAKE_ARGS);
×
UNCOV
1124
    UnixManagerRegisterCommand(
×
UNCOV
1125
            "dataset-lookup", UnixSocketDatasetLookup, &command, UNIX_CMD_TAKE_ARGS);
×
1126

UNCOV
1127
    return 0;
×
UNCOV
1128
}
×
1129

1130
typedef struct UnixManagerThreadData_ {
1131
    int padding;
1132
} UnixManagerThreadData;
1133

1134
static TmEcode UnixManagerThreadInit(ThreadVars *t, const void *initdata, void **data)
UNCOV
1135
{
×
UNCOV
1136
    UnixManagerThreadData *utd = SCCalloc(1, sizeof(*utd));
×
UNCOV
1137
    if (utd == NULL)
×
1138
        return TM_ECODE_FAILED;
×
1139

UNCOV
1140
    *data = utd;
×
UNCOV
1141
    return TM_ECODE_OK;
×
UNCOV
1142
}
×
1143

1144
static TmEcode UnixManagerThreadDeinit(ThreadVars *t, void *data)
UNCOV
1145
{
×
UNCOV
1146
    SCFree(data);
×
UNCOV
1147
    return TM_ECODE_OK;
×
UNCOV
1148
}
×
1149

1150
static TmEcode UnixManager(ThreadVars *th_v, void *thread_data)
UNCOV
1151
{
×
1152
    /* set the thread name */
UNCOV
1153
    SCLogDebug("%s started...", th_v->name);
×
1154

1155
    /* Set the threads capability */
UNCOV
1156
    th_v->cap_flags = 0;
×
UNCOV
1157
    SCDropCaps(th_v);
×
1158

UNCOV
1159
    TmThreadsSetFlag(th_v, THV_INIT_DONE | THV_RUNNING);
×
1160

UNCOV
1161
    while (1) {
×
UNCOV
1162
        int ret = UnixMain(&command);
×
UNCOV
1163
        if (ret == 0) {
×
1164
            SCLogError("Fatal error on unix socket");
×
1165
        }
×
1166

UNCOV
1167
        if ((ret == 0) || (TmThreadsCheckFlag(th_v, THV_KILL))) {
×
UNCOV
1168
            UnixClient *item;
×
UNCOV
1169
            UnixClient *titem;
×
UNCOV
1170
            TAILQ_FOREACH_SAFE(item, &(&command)->clients, next, titem) {
×
1171
                close(item->fd);
×
1172
                SCFree(item);
×
1173
            }
×
UNCOV
1174
            StatsSyncCounters(&th_v->stats);
×
UNCOV
1175
            break;
×
UNCOV
1176
        }
×
1177

UNCOV
1178
        UnixCommandBackgroundTasks(&command);
×
UNCOV
1179
    }
×
UNCOV
1180
    return TM_ECODE_OK;
×
UNCOV
1181
}
×
1182

1183
/** \brief Spawn the unix socket manager thread
1184
 *
1185
 * \param mode if set to 1, init failure cause suricata exit
1186
 * */
1187
void UnixManagerThreadSpawn(int mode)
UNCOV
1188
{
×
UNCOV
1189
    ThreadVars *tv_unixmgr = NULL;
×
1190

UNCOV
1191
    SCCtrlCondInit(&unix_manager_ctrl_cond, NULL);
×
UNCOV
1192
    SCCtrlMutexInit(&unix_manager_ctrl_mutex, NULL);
×
1193

UNCOV
1194
    tv_unixmgr = TmThreadCreateCmdThreadByName(thread_name_unix_socket,
×
UNCOV
1195
                                          "UnixManager", 0);
×
1196

UNCOV
1197
    if (tv_unixmgr == NULL) {
×
1198
        FatalError("TmThreadsCreate failed");
×
1199
    }
×
UNCOV
1200
    if (TmThreadSpawn(tv_unixmgr) != TM_ECODE_OK) {
×
1201
        FatalError("TmThreadSpawn failed");
×
1202
    }
×
UNCOV
1203
    if (mode == 1) {
×
UNCOV
1204
        if (TmThreadsCheckFlag(tv_unixmgr, THV_RUNNING_DONE)) {
×
1205
            FatalError("Unix socket init failed");
×
1206
        }
×
UNCOV
1207
    }
×
UNCOV
1208
}
×
1209

1210
// TODO can't think of a good name
1211
void UnixManagerThreadSpawnNonRunmode(const bool unix_socket)
UNCOV
1212
{
×
1213
    /* Spawn the unix socket manager thread */
UNCOV
1214
    if (unix_socket) {
×
UNCOV
1215
        if (UnixManagerInit() == 0) {
×
UNCOV
1216
            UnixManagerRegisterCommand("iface-stat", LiveDeviceIfaceStat, NULL,
×
UNCOV
1217
                    UNIX_CMD_TAKE_ARGS);
×
UNCOV
1218
            UnixManagerRegisterCommand("iface-list", LiveDeviceIfaceList, NULL, 0);
×
UNCOV
1219
            UnixManagerRegisterCommand("iface-bypassed-stat",
×
UNCOV
1220
                                       LiveDeviceGetBypassedStats, NULL, 0);
×
1221
            /* For backward compatibility */
UNCOV
1222
            UnixManagerRegisterCommand("ebpf-bypassed-stat",
×
UNCOV
1223
                                       LiveDeviceGetBypassedStats, NULL, 0);
×
UNCOV
1224
            UnixManagerThreadSpawn(0);
×
UNCOV
1225
        }
×
UNCOV
1226
    }
×
UNCOV
1227
}
×
1228

1229
/**
1230
 * \brief Used to kill unix manager thread(s).
1231
 *
1232
 * \todo Kinda hackish since it uses the tv name to identify unix manager
1233
 *       thread.  We need an all weather identification scheme.
1234
 */
1235
void UnixSocketKillSocketThread(void)
UNCOV
1236
{
×
UNCOV
1237
    ThreadVars *tv = NULL;
×
1238

UNCOV
1239
again:
×
UNCOV
1240
    SCMutexLock(&tv_root_lock);
×
1241

1242
    /* unix manager thread(s) is/are a part of command threads */
UNCOV
1243
    tv = tv_root[TVT_CMD];
×
1244

UNCOV
1245
    while (tv != NULL) {
×
UNCOV
1246
        if (strcasecmp(tv->name, "UnixManagerThread") == 0) {
×
1247
            /* If the thread dies during init it will have
1248
             * THV_RUNNING_DONE set, so we can set the correct flag
1249
             * and exit.
1250
             */
1251
            if (TmThreadsCheckFlag(tv, THV_RUNNING_DONE)) {
×
1252
                TmThreadsSetFlag(tv, THV_KILL);
×
1253
                TmThreadsSetFlag(tv, THV_DEINIT);
×
1254
                TmThreadsSetFlag(tv, THV_CLOSED);
×
1255
                break;
×
1256
            }
×
1257
            TmThreadsSetFlag(tv, THV_KILL);
×
1258
            TmThreadsSetFlag(tv, THV_DEINIT);
×
1259
            /* Be sure it has shut down */
1260
            if (!TmThreadsCheckFlag(tv, THV_CLOSED)) {
×
1261
                SCMutexUnlock(&tv_root_lock);
×
1262
                usleep(100);
×
1263
                goto again;
×
1264
            }
×
1265
        }
×
UNCOV
1266
        tv = tv->next;
×
UNCOV
1267
    }
×
1268

UNCOV
1269
    SCMutexUnlock(&tv_root_lock);
×
UNCOV
1270
}
×
1271

1272
#else /* BUILD_UNIX_SOCKET */
1273

1274
void UnixManagerThreadSpawn(int mode)
1275
{
1276
    SCLogError("Unix socket is not compiled");
1277
}
1278

1279
void UnixSocketKillSocketThread(void)
1280
{
1281
}
1282

1283
void UnixManagerThreadSpawnNonRunmode(const bool unix_socket_enabled)
1284
{
1285
}
1286

1287
#endif /* BUILD_UNIX_SOCKET */
1288

1289
void TmModuleUnixManagerRegister (void)
1290
{
2✔
1291
#if defined(BUILD_UNIX_SOCKET) && defined(HAVE_SYS_UN_H) && defined(HAVE_SYS_STAT_H) && defined(HAVE_SYS_TYPES_H)
2✔
1292
    tmm_modules[TMM_UNIXMANAGER].name = "UnixManager";
2✔
1293
    tmm_modules[TMM_UNIXMANAGER].ThreadInit = UnixManagerThreadInit;
2✔
1294
    tmm_modules[TMM_UNIXMANAGER].ThreadDeinit = UnixManagerThreadDeinit;
2✔
1295
    tmm_modules[TMM_UNIXMANAGER].Management = UnixManager;
2✔
1296
    tmm_modules[TMM_UNIXMANAGER].cap_flags = 0;
2✔
1297
    tmm_modules[TMM_UNIXMANAGER].flags = TM_FLAG_COMMAND_TM;
2✔
1298
#endif /* BUILD_UNIX_SOCKET */
2✔
1299
}
2✔
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