Coverage Report

Created: 2025-12-31 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata7/src/unix-manager.c
Line
Count
Source
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.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
60
0
#define SOCKET_PATH LOCAL_STATE_DIR "/run/suricata/"
61
0
#define SOCKET_FILENAME "suricata-command.socket"
62
0
#define SOCKET_TARGET SOCKET_PATH SOCKET_FILENAME
63
64
SCCtrlCondT unix_manager_ctrl_cond;
65
SCCtrlMutex unix_manager_ctrl_mutex;
66
67
0
#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
83
0
#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)
107
0
{
108
0
    struct sockaddr_un addr;
109
0
    int len;
110
0
    int ret;
111
0
    int on = 1;
112
0
    char sockettarget[PATH_MAX];
113
0
    const char *socketname;
114
115
0
    this->start_timestamp = time(NULL);
116
0
    this->socket = -1;
117
0
    this->select_max = 0;
118
119
0
    TAILQ_INIT(&this->commands);
120
0
    TAILQ_INIT(&this->tasks);
121
0
    TAILQ_INIT(&this->clients);
122
123
0
    int check_dir = 0;
124
0
    if (ConfGet("unix-command.filename", &socketname) == 1) {
125
0
        if (PathIsAbsolute(socketname)) {
126
0
            strlcpy(sockettarget, socketname, sizeof(sockettarget));
127
0
        } else {
128
0
            snprintf(sockettarget, sizeof(sockettarget), "%s/%s",
129
0
                    SOCKET_PATH, socketname);
130
0
            check_dir = 1;
131
0
        }
132
0
    } else {
133
0
        strlcpy(sockettarget, SOCKET_TARGET, sizeof(sockettarget));
134
0
        check_dir = 1;
135
0
    }
136
0
    SCLogInfo("unix socket '%s'", sockettarget);
137
138
0
    if (check_dir) {
139
0
        struct stat stat_buf;
140
        /* coverity[toctou] */
141
0
        if (stat(SOCKET_PATH, &stat_buf) != 0) {
142
            /* coverity[toctou] */
143
0
            ret = SCMkDir(SOCKET_PATH, S_IRWXU|S_IXGRP|S_IRGRP);
144
0
            if (ret != 0) {
145
0
                int err = errno;
146
0
                if (err != EEXIST) {
147
0
                    SCLogError(
148
0
                            "failed to create socket directory %s: %s", SOCKET_PATH, strerror(err));
149
0
                    return 0;
150
0
                }
151
0
            } else {
152
0
                SCLogInfo("created socket directory %s", SOCKET_PATH);
153
0
            }
154
0
        }
155
0
    }
156
157
    /* Remove socket file */
158
0
    (void) unlink(sockettarget);
159
160
    /* set address */
161
0
    addr.sun_family = AF_UNIX;
162
0
    strlcpy(addr.sun_path, sockettarget, sizeof(addr.sun_path));
163
0
    addr.sun_path[sizeof(addr.sun_path) - 1] = 0;
164
0
    len = strlen(addr.sun_path) + sizeof(addr.sun_family) + 1;
165
166
    /* create socket */
167
0
    this->socket = socket(AF_UNIX, SOCK_STREAM, 0);
168
0
    if (this->socket == -1) {
169
0
        SCLogWarning(
170
0
                "Unix Socket: unable to create UNIX socket %s: %s", addr.sun_path, strerror(errno));
171
0
        return 0;
172
0
    }
173
0
    this->select_max = this->socket + 1;
174
175
    /* set reuse option */
176
0
    ret = setsockopt(this->socket, SOL_SOCKET, SO_REUSEADDR,
177
0
                     (char *) &on, sizeof(on));
178
0
    if ( ret != 0 ) {
179
0
        SCLogWarning("Cannot set sockets options: %s.", strerror(errno));
180
0
    }
181
182
    /* bind socket */
183
0
    ret = bind(this->socket, (struct sockaddr *) &addr, len);
184
0
    if (ret == -1) {
185
0
        SCLogWarning("Unix socket: UNIX socket bind(%s) error: %s", sockettarget, strerror(errno));
186
0
        return 0;
187
0
    }
188
189
0
#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. */
193
0
    ret = chmod(sockettarget, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP);
194
0
    if (ret == -1) {
195
0
        int err = errno;
196
0
        SCLogWarning("Unable to change permission on socket: %s (%d)", strerror(err), err);
197
0
    }
198
0
#endif
199
200
    /* listen */
201
0
    if (listen(this->socket, 1) == -1) {
202
0
        SCLogWarning("Command server: UNIX socket listen() error: %s", strerror(errno));
203
0
        return 0;
204
0
    }
205
0
    return 1;
206
0
}
207
208
static void UnixCommandSetMaxFD(UnixCommand *this)
209
0
{
210
0
    UnixClient *item;
211
212
0
    if (this == NULL) {
213
0
        SCLogError("Unix command is NULL, warn devel");
214
0
        return;
215
0
    }
216
217
0
    this->select_max = this->socket + 1;
218
0
    TAILQ_FOREACH(item, &this->clients, next) {
219
0
        if (item->fd >= this->select_max) {
220
0
            this->select_max = item->fd + 1;
221
0
        }
222
0
    }
223
0
}
224
225
static UnixClient *UnixClientAlloc(void)
226
0
{
227
0
    UnixClient *uclient = SCMalloc(sizeof(UnixClient));
228
0
    if (unlikely(uclient == NULL)) {
229
0
        SCLogError("Can't allocate new client");
230
0
        return NULL;
231
0
    }
232
0
    uclient->mbuf = MemBufferCreateNew(CLIENT_BUFFER_SIZE);
233
0
    if (uclient->mbuf == NULL) {
234
0
        SCLogError("Can't allocate new client send buffer");
235
0
        SCFree(uclient);
236
0
        return NULL;
237
0
    }
238
0
    return uclient;
239
0
}
240
241
static void UnixClientFree(UnixClient *c)
242
0
{
243
0
    if (c != NULL) {
244
0
        MemBufferFree(c->mbuf);
245
0
        SCFree(c);
246
0
    }
247
0
}
248
249
/**
250
 * \brief Close the unix socket
251
 */
252
static void UnixCommandClose(UnixCommand  *this, int fd)
253
0
{
254
0
    UnixClient *item;
255
0
    UnixClient *safe = NULL;
256
0
    int found = 0;
257
258
0
    TAILQ_FOREACH_SAFE (item, &this->clients, next, safe) {
259
0
        if (item->fd == fd) {
260
0
            found = 1;
261
0
            break;
262
0
        }
263
0
    }
264
265
0
    if (found == 0) {
266
0
        SCLogError("No fd found in client list");
267
0
        return;
268
0
    }
269
270
0
    TAILQ_REMOVE(&this->clients, item, next);
271
272
0
    close(item->fd);
273
0
    UnixCommandSetMaxFD(this);
274
0
    UnixClientFree(item);
275
0
}
276
277
#define UNIX_PROTO_VERSION_LENGTH 200
278
0
#define UNIX_PROTO_VERSION_V1 "0.1"
279
0
#define UNIX_PROTO_V1 1
280
0
#define UNIX_PROTO_VERSION "0.2"
281
0
#define UNIX_PROTO_V2 2
282
283
static int UnixCommandSendJSONToClient(UnixClient *client, json_t *js)
284
0
{
285
0
    MemBufferReset(client->mbuf);
286
287
0
    OutputJSONMemBufferWrapper wrapper = {
288
0
        .buffer = &client->mbuf,
289
0
        .expand_by = CLIENT_BUFFER_SIZE
290
0
    };
291
292
0
    int r = json_dump_callback(js, OutputJSONMemBufferCallback, &wrapper,
293
0
            JSON_PRESERVE_ORDER|JSON_COMPACT|JSON_ENSURE_ASCII|
294
0
            JSON_ESCAPE_SLASH);
295
0
    if (r != 0) {
296
0
        SCLogWarning("unable to serialize JSON object");
297
0
        return -1;
298
0
    }
299
300
0
    if (client->version > UNIX_PROTO_V1) {
301
0
        if (MEMBUFFER_OFFSET(client->mbuf) + 1 >= MEMBUFFER_SIZE(client->mbuf)) {
302
0
            MemBufferExpand(&client->mbuf, 1);
303
0
        }
304
0
        MemBufferWriteString(client->mbuf, "\n");
305
0
    }
306
307
0
    if (send(client->fd, (const char *)MEMBUFFER_BUFFER(client->mbuf),
308
0
                MEMBUFFER_OFFSET(client->mbuf), MSG_NOSIGNAL) == -1)
309
0
    {
310
0
        SCLogWarning("unable to send block of size "
311
0
                     "%" PRIuMAX ": %s",
312
0
                (uintmax_t)MEMBUFFER_OFFSET(client->mbuf), strerror(errno));
313
0
        return -1;
314
0
    }
315
316
0
    SCLogDebug("sent message of size %"PRIuMAX" to client socket %d",
317
0
            (uintmax_t)MEMBUFFER_OFFSET(client->mbuf), client->fd);
318
0
    return 0;
319
0
}
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)
331
0
{
332
0
    char buffer[UNIX_PROTO_VERSION_LENGTH + 1];
333
0
    json_t *client_msg;
334
0
    json_t *server_msg;
335
0
    json_t *version;
336
0
    json_error_t jerror;
337
0
    int client;
338
0
    int client_version;
339
0
    int ret;
340
0
    UnixClient *uclient = NULL;
341
342
    /* accept client socket */
343
0
    socklen_t len = sizeof(this->client_addr);
344
0
    client = accept(this->socket, (struct sockaddr *) &this->client_addr,
345
0
                          &len);
346
0
    if (client < 0) {
347
0
        SCLogInfo("Unix socket: accept() error: %s",
348
0
                  strerror(errno));
349
0
        return 0;
350
0
    }
351
0
    SCLogDebug("Unix socket: client connection");
352
353
    /* read client version */
354
0
    buffer[sizeof(buffer)-1] = 0;
355
0
    ret = recv(client, buffer, sizeof(buffer)-1, 0);
356
0
    if (ret < 0) {
357
0
        SCLogInfo("Command server: client doesn't send version");
358
0
        close(client);
359
0
        return 0;
360
0
    }
361
0
    if (ret >= (int)(sizeof(buffer)-1)) {
362
0
        SCLogInfo("Command server: client message is too long, "
363
0
                  "disconnect him.");
364
0
        close(client);
365
0
        return 0;
366
0
    }
367
0
    buffer[ret] = 0;
368
369
0
    client_msg = json_loads(buffer, 0, &jerror);
370
0
    if (client_msg == NULL) {
371
0
        SCLogInfo("Invalid command, error on line %d: %s\n", jerror.line, jerror.text);
372
0
        close(client);
373
0
        return 0;
374
0
    }
375
376
0
    version = json_object_get(client_msg, "version");
377
0
    if (!json_is_string(version)) {
378
0
        SCLogInfo("error: version is not a string");
379
0
        close(client);
380
0
        json_decref(client_msg);
381
0
        return 0;
382
0
    }
383
384
    /* check client version */
385
0
    if ((strcmp(json_string_value(version), UNIX_PROTO_VERSION) != 0)
386
0
        && (strcmp(json_string_value(version), UNIX_PROTO_VERSION_V1) != 0)) {
387
0
        SCLogInfo("Unix socket: invalid client version: \"%s\"",
388
0
                json_string_value(version));
389
0
        json_decref(client_msg);
390
0
        close(client);
391
0
        return 0;
392
0
    } else {
393
0
        SCLogDebug("Unix socket: client version: \"%s\"",
394
0
                json_string_value(version));
395
0
        if (strcmp(json_string_value(version), UNIX_PROTO_VERSION_V1) == 0) {
396
0
            client_version = UNIX_PROTO_V1;
397
0
        } else {
398
0
            client_version = UNIX_PROTO_V2;
399
0
        }
400
0
    }
401
402
0
    json_decref(client_msg);
403
    /* send answer */
404
0
    server_msg = json_object();
405
0
    if (server_msg == NULL) {
406
0
        close(client);
407
0
        return 0;
408
0
    }
409
0
    json_object_set_new(server_msg, "return", json_string("OK"));
410
411
0
    uclient = UnixClientAlloc();
412
0
    if (unlikely(uclient == NULL)) {
413
0
        json_decref(server_msg);
414
0
        close(client);
415
0
        return 0;
416
0
    }
417
0
    uclient->fd = client;
418
0
    uclient->version = client_version;
419
420
0
    if (UnixCommandSendJSONToClient(uclient, server_msg) != 0) {
421
0
        SCLogWarning("Unable to send command");
422
423
0
        UnixClientFree(uclient);
424
0
        json_decref(server_msg);
425
0
        close(client);
426
0
        return 0;
427
0
    }
428
429
0
    json_decref(server_msg);
430
431
    /* client connected */
432
0
    SCLogDebug("Unix socket: client connected");
433
0
    TAILQ_INSERT_TAIL(&this->clients, uclient, next);
434
0
    UnixCommandSetMaxFD(this);
435
0
    return 1;
436
0
}
437
438
static int UnixCommandBackgroundTasks(UnixCommand* this)
439
0
{
440
0
    int ret = 1;
441
0
    Task *ltask;
442
443
0
    TAILQ_FOREACH(ltask, &this->tasks, next) {
444
0
        int fret = ltask->Func(ltask->data);
445
0
        if (fret != TM_ECODE_OK) {
446
0
            ret = 0;
447
0
        }
448
0
    }
449
0
    return ret;
450
0
}
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)
462
0
{
463
0
    int ret = 1;
464
0
    json_error_t error;
465
0
    json_t *jsoncmd = NULL;
466
0
    json_t *cmd = NULL;
467
0
    json_t *server_msg = json_object();
468
0
    const char * value;
469
0
    int found = 0;
470
0
    Command *lcmd;
471
472
0
    if (server_msg == NULL) {
473
0
        return 0;
474
0
    }
475
476
0
    jsoncmd = json_loads(command, 0, &error);
477
0
    if (jsoncmd == NULL) {
478
0
        SCLogInfo("Invalid command, error on line %d: %s\n", error.line, error.text);
479
0
        goto error;
480
0
    }
481
482
0
    cmd = json_object_get(jsoncmd, "command");
483
0
    if(!json_is_string(cmd)) {
484
0
        SCLogInfo("error: command is not a string");
485
0
        goto error_cmd;
486
0
    }
487
0
    value = json_string_value(cmd);
488
489
0
    TAILQ_FOREACH(lcmd, &this->commands, next) {
490
0
        if (!strcmp(value, lcmd->name)) {
491
0
            int fret = TM_ECODE_OK;
492
0
            found = 1;
493
0
            if (lcmd->flags & UNIX_CMD_TAKE_ARGS) {
494
0
                cmd = json_object_get(jsoncmd, "arguments");
495
0
                if(!json_is_object(cmd)) {
496
0
                    SCLogInfo("error: argument is not an object");
497
0
                    goto error_cmd;
498
0
                }
499
0
            }
500
0
            fret = lcmd->Func(cmd, server_msg, lcmd->data);
501
0
            if (fret != TM_ECODE_OK) {
502
0
                ret = 0;
503
0
            }
504
0
            break;
505
0
        }
506
0
    }
507
508
0
    if (found == 0) {
509
0
        json_object_set_new(server_msg, "message", json_string("Unknown command"));
510
0
        ret = 0;
511
0
    }
512
513
0
    switch (ret) {
514
0
        case 0:
515
0
            json_object_set_new(server_msg, "return", json_string("NOK"));
516
0
            break;
517
0
        case 1:
518
0
            json_object_set_new(server_msg, "return", json_string("OK"));
519
0
            break;
520
0
    }
521
522
0
    if (UnixCommandSendJSONToClient(client, server_msg) != 0) {
523
0
        goto error_cmd;
524
0
    }
525
526
0
    json_decref(jsoncmd);
527
0
    json_decref(server_msg);
528
0
    return ret;
529
530
0
error_cmd:
531
0
    json_decref(jsoncmd);
532
0
error:
533
0
    json_decref(server_msg);
534
0
    UnixCommandClose(this, client->fd);
535
0
    return 0;
536
0
}
537
538
static void UnixCommandRun(UnixCommand * this, UnixClient *client)
539
0
{
540
0
    char buffer[4096];
541
0
    int ret;
542
0
    if (client->version <= UNIX_PROTO_V1) {
543
0
        ret = recv(client->fd, buffer, sizeof(buffer) - 1, 0);
544
0
        if (ret <= 0) {
545
0
            if (ret == 0) {
546
0
                SCLogDebug("Unix socket: lost connection with client");
547
0
            } else {
548
0
                SCLogError("Unix socket: error on recv() from client: %s", strerror(errno));
549
0
            }
550
0
            UnixCommandClose(this, client->fd);
551
0
            return;
552
0
        }
553
0
        if (ret >= (int)(sizeof(buffer)-1)) {
554
0
            SCLogError("Command server: client command is too long, "
555
0
                       "disconnect him.");
556
0
            UnixCommandClose(this, client->fd);
557
0
            return;
558
0
        }
559
0
        buffer[ret] = 0;
560
0
    } else {
561
0
        int try = 0;
562
0
        int offset = 0;
563
0
        int cmd_over = 0;
564
0
        ret = recv(client->fd, buffer + offset, sizeof(buffer) - offset - 1, 0);
565
0
        do {
566
0
            if (ret <= 0) {
567
0
                if (ret == 0) {
568
0
                    SCLogDebug("Unix socket: lost connection with client");
569
0
                } else {
570
0
                    SCLogError("Unix socket: error on recv() from client: %s", strerror(errno));
571
0
                }
572
0
                UnixCommandClose(this, client->fd);
573
0
                return;
574
0
            }
575
0
            if (ret >= (int)(sizeof(buffer)- offset - 1)) {
576
0
                SCLogInfo("Command server: client command is too long, "
577
0
                        "disconnect him.");
578
0
                UnixCommandClose(this, client->fd);
579
0
                return;
580
0
            }
581
0
            if (buffer[ret - 1] == '\n') {
582
0
                buffer[ret-1] = 0;
583
0
                cmd_over = 1;
584
0
            } else {
585
0
                struct timeval tv;
586
0
                fd_set select_set;
587
0
                offset += ret;
588
0
                do {
589
0
                    FD_ZERO(&select_set);
590
0
                    FD_SET(client->fd, &select_set);
591
0
                    tv.tv_sec = 0;
592
0
                    tv.tv_usec = 200 * 1000;
593
0
                    try++;
594
0
                    ret = select(client->fd, &select_set, NULL, NULL, &tv);
595
                    /* catch select() error */
596
0
                    if (ret == -1) {
597
                        /* Signal was caught: just ignore it */
598
0
                        if (errno != EINTR) {
599
0
                            SCLogInfo("Unix socket: lost connection with client");
600
0
                            UnixCommandClose(this, client->fd);
601
0
                            return;
602
0
                        }
603
0
                    }
604
0
                } while (ret == 0 && try < 3);
605
0
                if (ret > 0) {
606
0
                    ret = recv(client->fd, buffer + offset,
607
0
                               sizeof(buffer) - offset - 1, 0);
608
0
                }
609
0
            }
610
0
        } while (try < 3 && cmd_over == 0);
611
612
0
        if (try == 3 && cmd_over == 0) {
613
0
            SCLogInfo("Unix socket: incomplete client message, closing connection");
614
0
            UnixCommandClose(this, client->fd);
615
0
            return;
616
0
        }
617
0
    }
618
0
    UnixCommandExecute(this, buffer, client);
619
0
}
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)
627
0
{
628
0
    struct timeval tv;
629
0
    int ret;
630
0
    fd_set select_set;
631
0
    UnixClient *uclient;
632
0
    UnixClient *tclient;
633
634
0
    if (suricata_ctl_flags & SURICATA_STOP) {
635
0
        TAILQ_FOREACH_SAFE (uclient, &this->clients, next, tclient) {
636
0
            UnixCommandClose(this, uclient->fd);
637
0
        }
638
0
        return 1;
639
0
    }
640
641
    /* Wait activity on the socket */
642
0
    FD_ZERO(&select_set);
643
0
    FD_SET(this->socket, &select_set);
644
0
    TAILQ_FOREACH(uclient, &this->clients, next) {
645
0
        FD_SET(uclient->fd, &select_set);
646
0
    }
647
648
0
    tv.tv_sec = 0;
649
0
    tv.tv_usec = 200 * 1000;
650
0
    ret = select(this->select_max, &select_set, NULL, NULL, &tv);
651
652
    /* catch select() error */
653
0
    if (ret == -1) {
654
        /* Signal was caught: just ignore it */
655
0
        if (errno == EINTR) {
656
0
            return 1;
657
0
        }
658
0
        SCLogError("Command server: select() fatal error: %s", strerror(errno));
659
0
        return 0;
660
0
    }
661
662
    /* timeout: continue */
663
0
    if (ret == 0) {
664
0
        return 1;
665
0
    }
666
667
0
    TAILQ_FOREACH_SAFE(uclient, &this->clients, next, tclient) {
668
0
        if (FD_ISSET(uclient->fd, &select_set)) {
669
0
            UnixCommandRun(this, uclient);
670
0
        }
671
0
    }
672
0
    if (FD_ISSET(this->socket, &select_set)) {
673
0
        if (!UnixCommandAccept(this))
674
0
            return 1;
675
0
    }
676
677
0
    return 1;
678
0
}
679
680
static TmEcode UnixManagerShutdownCommand(json_t *cmd,
681
                                   json_t *server_msg, void *data)
682
0
{
683
0
    SCEnter();
684
0
    json_object_set_new(server_msg, "message", json_string("Closing Suricata"));
685
0
    EngineStop();
686
0
    SCReturnInt(TM_ECODE_OK);
687
0
}
688
689
static TmEcode UnixManagerVersionCommand(json_t *cmd,
690
                                   json_t *server_msg, void *data)
691
0
{
692
0
    SCEnter();
693
0
    json_object_set_new(server_msg, "message", json_string(GetProgramVersion()));
694
0
    SCReturnInt(TM_ECODE_OK);
695
0
}
696
697
static TmEcode UnixManagerUptimeCommand(json_t *cmd,
698
                                 json_t *server_msg, void *data)
699
0
{
700
0
    SCEnter();
701
0
    int uptime;
702
0
    UnixCommand *ucmd = (UnixCommand *)data;
703
704
0
    uptime = time(NULL) - ucmd->start_timestamp;
705
0
    json_object_set_new(server_msg, "message", json_integer(uptime));
706
0
    SCReturnInt(TM_ECODE_OK);
707
0
}
708
709
static TmEcode UnixManagerRunningModeCommand(json_t *cmd,
710
                                      json_t *server_msg, void *data)
711
0
{
712
0
    SCEnter();
713
0
    json_object_set_new(server_msg, "message", json_string(RunmodeGetActive()));
714
0
    SCReturnInt(TM_ECODE_OK);
715
0
}
716
717
static TmEcode UnixManagerCaptureModeCommand(json_t *cmd,
718
                                      json_t *server_msg, void *data)
719
0
{
720
0
    SCEnter();
721
0
    json_object_set_new(server_msg, "message", json_string(RunModeGetMainMode()));
722
0
    SCReturnInt(TM_ECODE_OK);
723
0
}
724
725
static TmEcode UnixManagerReloadRulesWrapper(json_t *cmd, json_t *server_msg, void *data, int do_wait)
726
0
{
727
0
    SCEnter();
728
729
0
    if (SuriHasSigFile()) {
730
0
        json_object_set_new(server_msg, "message",
731
0
                            json_string("Live rule reload not possible if -s "
732
0
                                        "or -S option used at runtime."));
733
0
        SCReturnInt(TM_ECODE_FAILED);
734
0
    }
735
736
0
    int r = DetectEngineReloadStart();
737
738
0
    if (r == 0 && do_wait) {
739
0
        while (!DetectEngineReloadIsIdle())
740
0
            usleep(100);
741
0
    } else {
742
0
        if (r == -1) {
743
0
            json_object_set_new(server_msg, "message", json_string("Reload already in progress"));
744
0
            SCReturnInt(TM_ECODE_FAILED);
745
0
        }
746
0
    }
747
748
0
    json_object_set_new(server_msg, "message", json_string("done"));
749
0
    SCReturnInt(TM_ECODE_OK);
750
0
}
751
752
static TmEcode UnixManagerReloadRules(json_t *cmd, json_t *server_msg, void *data)
753
0
{
754
0
    return UnixManagerReloadRulesWrapper(cmd, server_msg, data, 1);
755
0
}
756
757
static TmEcode UnixManagerNonBlockingReloadRules(json_t *cmd, json_t *server_msg, void *data)
758
0
{
759
0
    return UnixManagerReloadRulesWrapper(cmd, server_msg, data, 0);
760
0
}
761
762
static TmEcode UnixManagerReloadTimeCommand(json_t *cmd,
763
                                            json_t *server_msg, void *data)
764
0
{
765
0
    SCEnter();
766
0
    TmEcode retval;
767
0
    json_t *jdata = NULL;
768
769
0
    retval = OutputEngineStatsReloadTime(&jdata);
770
0
    json_object_set_new(server_msg, "message", jdata);
771
0
    SCReturnInt(retval);
772
0
}
773
774
static TmEcode UnixManagerRulesetStatsCommand(json_t *cmd,
775
                                              json_t *server_msg, void *data)
776
0
{
777
0
    SCEnter();
778
0
    TmEcode retval;
779
0
    json_t *jdata = NULL;
780
781
0
    retval = OutputEngineStatsRuleset(&jdata);
782
0
    json_object_set_new(server_msg, "message", jdata);
783
0
    SCReturnInt(retval);
784
0
}
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
0
{
821
0
    SCEnter();
822
0
    int rules_cnt = 0;
823
0
    DetectEngineCtx *de_ctx = DetectEngineGetCurrent();
824
0
    if (de_ctx == NULL) {
825
0
        json_object_set_new(server_msg, "message", json_string("Unable to get info"));
826
0
        SCReturnInt(TM_ECODE_OK);
827
0
    }
828
829
    /* Since we need to deference de_ctx, we don't want to lost it. */
830
0
    DetectEngineCtx *list = de_ctx;
831
0
    json_t *js_sigs_array = json_array();
832
833
0
    if (js_sigs_array == NULL) {
834
0
        json_object_set_new(server_msg, "message", json_string("Unable to get info"));
835
0
        goto error;
836
0
    }
837
0
    while (list) {
838
0
        SigString *sigs_str = NULL;
839
0
        TAILQ_FOREACH(sigs_str, &list->sig_stat.failed_sigs, next) {
840
0
            json_t *jdata = json_object();
841
0
            if (jdata == NULL) {
842
0
                json_object_set_new(server_msg, "message", json_string("Unable to get the sig"));
843
0
                goto error;
844
0
            }
845
846
0
            json_object_set_new(jdata, "tenant_id", json_integer(list->tenant_id));
847
0
            json_object_set_new(jdata, "rule", json_string(sigs_str->sig_str));
848
0
            json_object_set_new(jdata, "filename", json_string(sigs_str->filename));
849
0
            json_object_set_new(jdata, "line", json_integer(sigs_str->line));
850
0
            if (sigs_str->sig_error) {
851
0
                json_object_set_new(jdata, "error", json_string(sigs_str->sig_error));
852
0
            }
853
0
            json_array_append_new(js_sigs_array, jdata);
854
0
            if (++rules_cnt > MAX_FAILED_RULES) {
855
0
                break;
856
0
            }
857
0
        }
858
0
        if (rules_cnt > MAX_FAILED_RULES) {
859
0
            break;
860
0
        }
861
0
        list = list->next;
862
0
    }
863
864
0
    json_object_set_new(server_msg, "message", js_sigs_array);
865
0
    DetectEngineDeReference(&de_ctx);
866
0
    SCReturnInt(TM_ECODE_OK);
867
868
0
error:
869
0
    DetectEngineDeReference(&de_ctx);
870
0
    json_object_clear(js_sigs_array);
871
0
    json_decref(js_sigs_array);
872
0
    SCReturnInt(TM_ECODE_FAILED);
873
0
}
874
875
static TmEcode UnixManagerConfGetCommand(json_t *cmd,
876
                                         json_t *server_msg, void *data)
877
0
{
878
0
    SCEnter();
879
880
0
    const char *confval = NULL;
881
0
    char *variable = NULL;
882
883
0
    json_t *jarg = json_object_get(cmd, "variable");
884
0
    if(!json_is_string(jarg)) {
885
0
        SCLogInfo("error: variable is not a string");
886
0
        json_object_set_new(server_msg, "message", json_string("variable is not a string"));
887
0
        SCReturnInt(TM_ECODE_FAILED);
888
0
    }
889
890
0
    variable = (char *)json_string_value(jarg);
891
0
    if (ConfGet(variable, &confval) != 1) {
892
0
        json_object_set_new(server_msg, "message", json_string("Unable to get value"));
893
0
        SCReturnInt(TM_ECODE_FAILED);
894
0
    }
895
896
0
    if (confval) {
897
0
        json_object_set_new(server_msg, "message", json_string(confval));
898
0
        SCReturnInt(TM_ECODE_OK);
899
0
    }
900
901
0
    json_object_set_new(server_msg, "message", json_string("No string value"));
902
0
    SCReturnInt(TM_ECODE_FAILED);
903
0
}
904
905
static TmEcode UnixManagerListCommand(json_t *cmd,
906
                               json_t *answer, void *data)
907
0
{
908
0
    SCEnter();
909
0
    json_t *jdata;
910
0
    json_t *jarray;
911
0
    Command *lcmd = NULL;
912
0
    UnixCommand *gcmd = (UnixCommand *) data;
913
0
    int i = 0;
914
915
0
    jdata = json_object();
916
0
    if (jdata == NULL) {
917
0
        json_object_set_new(answer, "message",
918
0
                            json_string("internal error at json object creation"));
919
0
        return TM_ECODE_FAILED;
920
0
    }
921
0
    jarray = json_array();
922
0
    if (jarray == NULL) {
923
0
        json_object_set_new(answer, "message",
924
0
                            json_string("internal error at json object creation"));
925
0
        return TM_ECODE_FAILED;
926
0
    }
927
928
0
    TAILQ_FOREACH(lcmd, &gcmd->commands, next) {
929
0
        json_array_append_new(jarray, json_string(lcmd->name));
930
0
        i++;
931
0
    }
932
933
0
    json_object_set_new(jdata, "count", json_integer(i));
934
0
    json_object_set_new(jdata, "commands", jarray);
935
0
    json_object_set_new(answer, "message", jdata);
936
0
    SCReturnInt(TM_ECODE_OK);
937
0
}
938
939
static TmEcode UnixManagerReopenLogFiles(json_t *cmd, json_t *server_msg, void *data)
940
0
{
941
0
    OutputNotifyFileRotation();
942
0
    json_object_set_new(server_msg, "message", json_string("done"));
943
0
    SCReturnInt(TM_ECODE_OK);
944
0
}
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)
987
0
{
988
0
    SCEnter();
989
0
    Command *cmd = NULL;
990
0
    Command *lcmd = NULL;
991
992
0
    if (Func == NULL) {
993
0
        SCLogError("Null function");
994
0
        SCReturnInt(TM_ECODE_FAILED);
995
0
    }
996
997
0
    if (keyword == NULL) {
998
0
        SCLogError("Null keyword");
999
0
        SCReturnInt(TM_ECODE_FAILED);
1000
0
    }
1001
1002
0
    TAILQ_FOREACH(lcmd, &command.commands, next) {
1003
0
        if (!strcmp(keyword, lcmd->name)) {
1004
0
            SCLogError("%s already registered", keyword);
1005
0
            SCReturnInt(TM_ECODE_FAILED);
1006
0
        }
1007
0
    }
1008
1009
0
    cmd = SCMalloc(sizeof(Command));
1010
0
    if (unlikely(cmd == NULL)) {
1011
0
        SCLogError("Can't alloc cmd");
1012
0
        SCReturnInt(TM_ECODE_FAILED);
1013
0
    }
1014
0
    cmd->name = SCStrdup(keyword);
1015
0
    if (unlikely(cmd->name == NULL)) {
1016
0
        SCLogError("Can't alloc cmd name");
1017
0
        SCFree(cmd);
1018
0
        SCReturnInt(TM_ECODE_FAILED);
1019
0
    }
1020
0
    cmd->Func = Func;
1021
0
    cmd->data = data;
1022
0
    cmd->flags = flags;
1023
    /* Add it to the list */
1024
0
    TAILQ_INSERT_TAIL(&command.commands, cmd, next);
1025
1026
0
    SCReturnInt(TM_ECODE_OK);
1027
0
}
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)
1041
0
{
1042
0
    SCEnter();
1043
0
    Task *task = NULL;
1044
1045
0
    if (Func == NULL) {
1046
0
        SCLogError("Null function");
1047
0
        SCReturnInt(TM_ECODE_FAILED);
1048
0
    }
1049
1050
0
    task = SCMalloc(sizeof(Task));
1051
0
    if (unlikely(task == NULL)) {
1052
0
        SCLogError("Can't alloc task");
1053
0
        SCReturnInt(TM_ECODE_FAILED);
1054
0
    }
1055
0
    task->Func = Func;
1056
0
    task->data = data;
1057
    /* Add it to the list */
1058
0
    TAILQ_INSERT_TAIL(&command.tasks, task, next);
1059
1060
0
    SCReturnInt(TM_ECODE_OK);
1061
0
}
1062
1063
int UnixManagerInit(void)
1064
0
{
1065
0
    if (UnixNew(&command) == 0) {
1066
0
        int failure_fatal = 0;
1067
0
        if (ConfGetBool("engine.init-failure-fatal", &failure_fatal) != 1) {
1068
0
            SCLogDebug("ConfGetBool could not load the value.");
1069
0
        }
1070
0
        if (failure_fatal) {
1071
0
            FatalError("Unable to create unix command socket");
1072
0
        } else {
1073
0
            SCLogWarning("Unable to create unix command socket");
1074
0
            return -1;
1075
0
        }
1076
0
    }
1077
1078
    /* Init Unix socket */
1079
0
    UnixManagerRegisterCommand("shutdown", UnixManagerShutdownCommand, NULL, 0);
1080
0
    UnixManagerRegisterCommand("command-list", UnixManagerListCommand, &command, 0);
1081
0
    UnixManagerRegisterCommand("help", UnixManagerListCommand, &command, 0);
1082
0
    UnixManagerRegisterCommand("version", UnixManagerVersionCommand, &command, 0);
1083
0
    UnixManagerRegisterCommand("uptime", UnixManagerUptimeCommand, &command, 0);
1084
0
    UnixManagerRegisterCommand("running-mode", UnixManagerRunningModeCommand, &command, 0);
1085
0
    UnixManagerRegisterCommand("capture-mode", UnixManagerCaptureModeCommand, &command, 0);
1086
0
    UnixManagerRegisterCommand("conf-get", UnixManagerConfGetCommand, &command, UNIX_CMD_TAKE_ARGS);
1087
0
    UnixManagerRegisterCommand("dump-counters", StatsOutputCounterSocket, NULL, 0);
1088
0
    UnixManagerRegisterCommand("reload-rules", UnixManagerReloadRules, NULL, 0);
1089
0
    UnixManagerRegisterCommand("ruleset-reload-rules", UnixManagerReloadRules, NULL, 0);
1090
0
    UnixManagerRegisterCommand("ruleset-reload-nonblocking", UnixManagerNonBlockingReloadRules, NULL, 0);
1091
0
    UnixManagerRegisterCommand("ruleset-reload-time", UnixManagerReloadTimeCommand, NULL, 0);
1092
0
    UnixManagerRegisterCommand("ruleset-stats", UnixManagerRulesetStatsCommand, NULL, 0);
1093
0
    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
1101
0
    UnixManagerRegisterCommand("register-tenant-handler", UnixSocketRegisterTenantHandler, &command, UNIX_CMD_TAKE_ARGS);
1102
0
    UnixManagerRegisterCommand("unregister-tenant-handler", UnixSocketUnregisterTenantHandler, &command, UNIX_CMD_TAKE_ARGS);
1103
0
    UnixManagerRegisterCommand("register-tenant", UnixSocketRegisterTenant, &command, UNIX_CMD_TAKE_ARGS);
1104
0
    UnixManagerRegisterCommand("reload-tenant", UnixSocketReloadTenant, &command, UNIX_CMD_TAKE_ARGS);
1105
0
    UnixManagerRegisterCommand("reload-tenants", UnixSocketReloadTenants, &command, 0);
1106
0
    UnixManagerRegisterCommand("unregister-tenant", UnixSocketUnregisterTenant, &command, UNIX_CMD_TAKE_ARGS);
1107
0
    UnixManagerRegisterCommand("add-hostbit", UnixSocketHostbitAdd, &command, UNIX_CMD_TAKE_ARGS);
1108
0
    UnixManagerRegisterCommand("remove-hostbit", UnixSocketHostbitRemove, &command, UNIX_CMD_TAKE_ARGS);
1109
0
    UnixManagerRegisterCommand("list-hostbit", UnixSocketHostbitList, &command, UNIX_CMD_TAKE_ARGS);
1110
0
    UnixManagerRegisterCommand("reopen-log-files", UnixManagerReopenLogFiles, NULL, 0);
1111
0
    UnixManagerRegisterCommand("memcap-set", UnixSocketSetMemcap, &command, UNIX_CMD_TAKE_ARGS);
1112
0
    UnixManagerRegisterCommand("memcap-show", UnixSocketShowMemcap, &command, UNIX_CMD_TAKE_ARGS);
1113
0
    UnixManagerRegisterCommand("memcap-list", UnixSocketShowAllMemcap, NULL, 0);
1114
1115
0
    UnixManagerRegisterCommand("dataset-add", UnixSocketDatasetAdd, &command, UNIX_CMD_TAKE_ARGS);
1116
0
    UnixManagerRegisterCommand("dataset-remove", UnixSocketDatasetRemove, &command, UNIX_CMD_TAKE_ARGS);
1117
0
    UnixManagerRegisterCommand(
1118
0
            "get-flow-stats-by-id", UnixSocketGetFlowStatsById, &command, UNIX_CMD_TAKE_ARGS);
1119
0
    UnixManagerRegisterCommand("dataset-dump", UnixSocketDatasetDump, NULL, 0);
1120
0
    UnixManagerRegisterCommand(
1121
0
            "dataset-clear", UnixSocketDatasetClear, &command, UNIX_CMD_TAKE_ARGS);
1122
0
    UnixManagerRegisterCommand(
1123
0
            "dataset-lookup", UnixSocketDatasetLookup, &command, UNIX_CMD_TAKE_ARGS);
1124
1125
0
    return 0;
1126
0
}
1127
1128
typedef struct UnixManagerThreadData_ {
1129
    int padding;
1130
} UnixManagerThreadData;
1131
1132
static TmEcode UnixManagerThreadInit(ThreadVars *t, const void *initdata, void **data)
1133
0
{
1134
0
    UnixManagerThreadData *utd = SCCalloc(1, sizeof(*utd));
1135
0
    if (utd == NULL)
1136
0
        return TM_ECODE_FAILED;
1137
1138
0
    *data = utd;
1139
0
    return TM_ECODE_OK;
1140
0
}
1141
1142
static TmEcode UnixManagerThreadDeinit(ThreadVars *t, void *data)
1143
0
{
1144
0
    SCFree(data);
1145
0
    return TM_ECODE_OK;
1146
0
}
1147
1148
static TmEcode UnixManager(ThreadVars *th_v, void *thread_data)
1149
0
{
1150
0
    int ret;
1151
1152
    /* set the thread name */
1153
0
    SCLogDebug("%s started...", th_v->name);
1154
1155
0
    StatsSetupPrivate(th_v);
1156
1157
    /* Set the threads capability */
1158
0
    th_v->cap_flags = 0;
1159
0
    SCDropCaps(th_v);
1160
1161
0
    TmThreadsSetFlag(th_v, THV_INIT_DONE | THV_RUNNING);
1162
1163
0
    while (1) {
1164
0
        ret = UnixMain(&command);
1165
0
        if (ret == 0) {
1166
0
            SCLogError("Fatal error on unix socket");
1167
0
        }
1168
1169
0
        if ((ret == 0) || (TmThreadsCheckFlag(th_v, THV_KILL))) {
1170
0
            UnixClient *item;
1171
0
            UnixClient *titem;
1172
0
            TAILQ_FOREACH_SAFE(item, &(&command)->clients, next, titem) {
1173
0
                close(item->fd);
1174
0
                SCFree(item);
1175
0
            }
1176
0
            StatsSyncCounters(th_v);
1177
0
            break;
1178
0
        }
1179
1180
0
        UnixCommandBackgroundTasks(&command);
1181
0
    }
1182
0
    return TM_ECODE_OK;
1183
0
}
1184
1185
1186
/** \brief Spawn the unix socket manager thread
1187
 *
1188
 * \param mode if set to 1, init failure cause suricata exit
1189
 * */
1190
void UnixManagerThreadSpawn(int mode)
1191
0
{
1192
0
    ThreadVars *tv_unixmgr = NULL;
1193
1194
0
    SCCtrlCondInit(&unix_manager_ctrl_cond, NULL);
1195
0
    SCCtrlMutexInit(&unix_manager_ctrl_mutex, NULL);
1196
1197
0
    tv_unixmgr = TmThreadCreateCmdThreadByName(thread_name_unix_socket,
1198
0
                                          "UnixManager", 0);
1199
1200
0
    if (tv_unixmgr == NULL) {
1201
0
        FatalError("TmThreadsCreate failed");
1202
0
    }
1203
0
    if (TmThreadSpawn(tv_unixmgr) != TM_ECODE_OK) {
1204
0
        FatalError("TmThreadSpawn failed");
1205
0
    }
1206
0
    if (mode == 1) {
1207
0
        if (TmThreadsCheckFlag(tv_unixmgr, THV_RUNNING_DONE)) {
1208
0
            FatalError("Unix socket init failed");
1209
0
        }
1210
0
    }
1211
0
    return;
1212
0
}
1213
1214
// TODO can't think of a good name
1215
void UnixManagerThreadSpawnNonRunmode(const bool unix_socket)
1216
0
{
1217
    /* Spawn the unix socket manager thread */
1218
0
    if (unix_socket) {
1219
0
        if (UnixManagerInit() == 0) {
1220
0
            UnixManagerRegisterCommand("iface-stat", LiveDeviceIfaceStat, NULL,
1221
0
                    UNIX_CMD_TAKE_ARGS);
1222
0
            UnixManagerRegisterCommand("iface-list", LiveDeviceIfaceList, NULL, 0);
1223
0
            UnixManagerRegisterCommand("iface-bypassed-stat",
1224
0
                                       LiveDeviceGetBypassedStats, NULL, 0);
1225
            /* For backward compatibility */
1226
0
            UnixManagerRegisterCommand("ebpf-bypassed-stat",
1227
0
                                       LiveDeviceGetBypassedStats, NULL, 0);
1228
0
            UnixManagerThreadSpawn(0);
1229
0
        }
1230
0
    }
1231
0
}
1232
1233
/**
1234
 * \brief Used to kill unix manager thread(s).
1235
 *
1236
 * \todo Kinda hackish since it uses the tv name to identify unix manager
1237
 *       thread.  We need an all weather identification scheme.
1238
 */
1239
void UnixSocketKillSocketThread(void)
1240
0
{
1241
0
    ThreadVars *tv = NULL;
1242
1243
0
again:
1244
0
    SCMutexLock(&tv_root_lock);
1245
1246
    /* unix manager thread(s) is/are a part of command threads */
1247
0
    tv = tv_root[TVT_CMD];
1248
1249
0
    while (tv != NULL) {
1250
0
        if (strcasecmp(tv->name, "UnixManagerThread") == 0) {
1251
            /* If the thread dies during init it will have
1252
             * THV_RUNNING_DONE set, so we can set the correct flag
1253
             * and exit.
1254
             */
1255
0
            if (TmThreadsCheckFlag(tv, THV_RUNNING_DONE)) {
1256
0
                TmThreadsSetFlag(tv, THV_KILL);
1257
0
                TmThreadsSetFlag(tv, THV_DEINIT);
1258
0
                TmThreadsSetFlag(tv, THV_CLOSED);
1259
0
                break;
1260
0
            }
1261
0
            TmThreadsSetFlag(tv, THV_KILL);
1262
0
            TmThreadsSetFlag(tv, THV_DEINIT);
1263
            /* Be sure it has shut down */
1264
0
            if (!TmThreadsCheckFlag(tv, THV_CLOSED)) {
1265
0
                SCMutexUnlock(&tv_root_lock);
1266
0
                usleep(100);
1267
0
                goto again;
1268
0
            }
1269
0
        }
1270
0
        tv = tv->next;
1271
0
    }
1272
1273
0
    SCMutexUnlock(&tv_root_lock);
1274
0
    return;
1275
0
}
1276
1277
#else /* BUILD_UNIX_SOCKET */
1278
1279
void UnixManagerThreadSpawn(int mode)
1280
{
1281
    SCLogError("Unix socket is not compiled");
1282
    return;
1283
}
1284
1285
void UnixSocketKillSocketThread(void)
1286
{
1287
    return;
1288
}
1289
1290
void UnixManagerThreadSpawnNonRunmode(const bool unix_socket_enabled)
1291
{
1292
    return;
1293
}
1294
1295
#endif /* BUILD_UNIX_SOCKET */
1296
1297
void TmModuleUnixManagerRegister (void)
1298
71
{
1299
71
#if defined(BUILD_UNIX_SOCKET) && defined(HAVE_SYS_UN_H) && defined(HAVE_SYS_STAT_H) && defined(HAVE_SYS_TYPES_H)
1300
71
    tmm_modules[TMM_UNIXMANAGER].name = "UnixManager";
1301
71
    tmm_modules[TMM_UNIXMANAGER].ThreadInit = UnixManagerThreadInit;
1302
71
    tmm_modules[TMM_UNIXMANAGER].ThreadDeinit = UnixManagerThreadDeinit;
1303
71
    tmm_modules[TMM_UNIXMANAGER].Management = UnixManager;
1304
71
    tmm_modules[TMM_UNIXMANAGER].cap_flags = 0;
1305
71
    tmm_modules[TMM_UNIXMANAGER].flags = TM_FLAG_COMMAND_TM;
1306
71
#endif /* BUILD_UNIX_SOCKET */
1307
71
}