mirror of
https://github.com/rsyslog/rsyslog.git
synced 2025-12-17 05:50:41 +01:00
also bump default development container to Ubuntu 24.04 This commit also contains some nitfixes that were detected by the newer CI components. If it is just nitfixes, we do not create separate commits for them.
485 lines
11 KiB
C
485 lines
11 KiB
C
/**
|
|
* logctl - a tool to access lumberjack logs in MongoDB
|
|
* ... and potentially other sources in the future.
|
|
*
|
|
* Copyright 2012-2022 Ulrike Gerhards and Adiscon GmbH.
|
|
*
|
|
* Copyright 2017 Hugo Soszynski and aDvens
|
|
*
|
|
* long short
|
|
|
|
* level l read records with level x
|
|
* severity s read records with severity x
|
|
* ret r number of records to return
|
|
* skip k number of records to skip
|
|
* sys y read records of system x
|
|
* msg m read records with message containing x
|
|
* datef f read records starting on time received x
|
|
* dateu u read records until time received x
|
|
*
|
|
* examples:
|
|
*
|
|
* logctl -f 15/05/2012-12:00:00 -u 15/05/2012-12:37:00
|
|
* logctl -s 50 --ret 10
|
|
* logctl -m "closed"
|
|
* logctl -l "INFO"
|
|
* logctl -s 3
|
|
* logctl -y "ubuntu"
|
|
*
|
|
* This file is part of rsyslog.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
* -or-
|
|
* see COPYING.ASL20 in the source distribution
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
#include "config.h"
|
|
#define _XOPEN_SOURCE 700 /* Need to define POSIX version to use strptime() */
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
#include <getopt.h>
|
|
#include <unistd.h>
|
|
|
|
#include "rsyslog.h"
|
|
/* we need this to avoid issues with older versions of libbson */
|
|
PRAGMA_DIAGNOSTIC_PUSH
|
|
PRAGMA_IGNORE_Wpragmas
|
|
PRAGMA_IGNORE_Wunknown_warning_option
|
|
PRAGMA_IGNORE_Wunknown_attribute
|
|
PRAGMA_IGNORE_Wexpansion_to_defined
|
|
PRAGMA_IGNORE_Wstrict_prototypes
|
|
PRAGMA_IGNORE_Wold_style_definition
|
|
#include <mongoc.h>
|
|
#include <bson.h>
|
|
PRAGMA_DIAGNOSTIC_POP
|
|
|
|
#define N 80
|
|
|
|
static struct option long_options[] =
|
|
{
|
|
{"level", required_argument, NULL, 'l'},
|
|
{"severity", required_argument, NULL, 's'},
|
|
{"ret", required_argument, NULL, 'r'},
|
|
{"skip", required_argument, NULL, 'k'},
|
|
{"sys", required_argument, NULL, 'y'},
|
|
{"msg", required_argument, NULL, 'm'},
|
|
{"datef", required_argument, NULL, 'f'},
|
|
{"dateu", required_argument, NULL, 'u'},
|
|
{NULL, 0, NULL, 0}
|
|
};
|
|
|
|
struct queryopt
|
|
{
|
|
int32_t e_sever;
|
|
int32_t e_ret;
|
|
int32_t e_skip;
|
|
char* e_date;
|
|
char* e_level;
|
|
char* e_msg;
|
|
char* e_sys;
|
|
char* e_dateu;
|
|
int bsever;
|
|
int blevel;
|
|
int bskip;
|
|
int bret;
|
|
int bsys;
|
|
int bmsg;
|
|
int bdate;
|
|
int bdatef;
|
|
int bdateu;
|
|
};
|
|
|
|
struct ofields
|
|
{
|
|
const char* msg;
|
|
const char* syslog_tag;
|
|
const char* prog;
|
|
char* date;
|
|
int64_t date_r;
|
|
};
|
|
|
|
struct query_doc
|
|
{
|
|
bson_t* query;
|
|
};
|
|
|
|
struct select_doc
|
|
{
|
|
bson_t* select;
|
|
};
|
|
|
|
struct db_connect
|
|
{
|
|
mongoc_client_t* conn;
|
|
};
|
|
|
|
struct db_collection
|
|
{
|
|
mongoc_collection_t* collection;
|
|
};
|
|
|
|
struct db_cursor
|
|
{
|
|
mongoc_cursor_t* cursor;
|
|
};
|
|
|
|
struct results
|
|
{
|
|
const bson_t* result;
|
|
};
|
|
|
|
|
|
static void formater(struct ofields* fields)
|
|
{
|
|
char str[N];
|
|
time_t rtime;
|
|
struct tm now;
|
|
|
|
rtime = (time_t) (fields->date_r / 1000);
|
|
strftime (str, N, "%b %d %H:%M:%S", gmtime_r (&rtime, &now));
|
|
printf ("%s %s %s %s\n", str, fields->prog, fields->syslog_tag,
|
|
fields->msg);
|
|
}
|
|
|
|
static struct ofields* get_data(struct results* res)
|
|
{
|
|
struct ofields* fields;
|
|
const char* msg;
|
|
const char* prog;
|
|
const char* syslog_tag;
|
|
int64_t date_r;
|
|
bson_iter_t c;
|
|
|
|
fields = malloc (sizeof (struct ofields));
|
|
bson_iter_init_find (&c, res->result, "msg");
|
|
if (!(msg = bson_iter_utf8 (&c, NULL)))
|
|
{
|
|
perror ("bson_cursor_get_string()");
|
|
exit (1);
|
|
}
|
|
|
|
bson_iter_init_find (&c, res->result, "sys");
|
|
if (!(prog = bson_iter_utf8 (&c, NULL)))
|
|
{
|
|
perror ("bson_cursor_get_string()");
|
|
exit (1);
|
|
}
|
|
|
|
bson_iter_init_find (&c, res->result, "syslog_tag");
|
|
if (!(syslog_tag = bson_iter_utf8 (&c, NULL)))
|
|
{
|
|
perror ("bson_cursor_get_string()");
|
|
exit (1);
|
|
}
|
|
|
|
bson_iter_init_find (&c, res->result, "time_rcvd");
|
|
if (!(date_r = bson_iter_date_time (&c)))
|
|
{
|
|
perror ("bson_cursor_get_utc_datetime()");
|
|
exit (1);
|
|
}
|
|
|
|
fields->msg = msg;
|
|
fields->prog = prog;
|
|
fields->syslog_tag = syslog_tag;
|
|
fields->date_r = date_r;
|
|
|
|
return fields;
|
|
}
|
|
|
|
static void getoptions(int argc, char* argv[], struct queryopt* opt)
|
|
{
|
|
int iarg;
|
|
|
|
while ((iarg = getopt_long (argc, argv, "l:s:r:k:y:f:u:m:",
|
|
long_options, NULL)) != -1)
|
|
{
|
|
/* check to see if a single character or long option came through */
|
|
switch (iarg)
|
|
{
|
|
/* short option 's' */
|
|
case 's':
|
|
opt->bsever = 1;
|
|
opt->e_sever = atoi (optarg);
|
|
break;
|
|
/* short option 'r' */
|
|
case 'r':
|
|
opt->bret = 1;
|
|
opt->e_ret = atoi (optarg);
|
|
break;
|
|
/* short option 'f' : date from */
|
|
case 'f':
|
|
opt->bdate = 1;
|
|
opt->bdatef = 1;
|
|
opt->e_date = optarg;
|
|
break;
|
|
/* short option 'u': date until */
|
|
case 'u':
|
|
opt->bdate = 1;
|
|
opt->bdateu = 1;
|
|
opt->e_dateu = optarg;
|
|
break;
|
|
/* short option 'k' */
|
|
case 'k':
|
|
opt->bskip = 1;
|
|
opt->e_skip = atoi (optarg);
|
|
break;
|
|
/* short option 'l' */
|
|
case 'l':
|
|
opt->blevel = 1;
|
|
opt->e_level = optarg;
|
|
break;
|
|
/* short option 'm' */
|
|
case 'm':
|
|
opt->bmsg = 1;
|
|
opt->e_msg = optarg;
|
|
break;
|
|
/* short option 'y' */
|
|
case 'y':
|
|
opt->bsys = 1;
|
|
opt->e_sys = optarg;
|
|
break;
|
|
default:
|
|
break;
|
|
} /* end switch iarg */
|
|
} /* end while */
|
|
|
|
} /* end void getoptions */
|
|
|
|
static struct select_doc* create_select(void)
|
|
/* BSON object indicating the fields to return */
|
|
{
|
|
struct select_doc* s_doc;
|
|
|
|
s_doc = malloc (sizeof (struct select_doc));
|
|
s_doc->select = bson_new ();
|
|
bson_append_utf8 (s_doc->select, "syslog_tag", 10, "s", 1);
|
|
bson_append_utf8 (s_doc->select, "msg", 3, "ERROR", 5);
|
|
bson_append_utf8 (s_doc->select, "sys", 3, "sys", 3);
|
|
bson_append_date_time (s_doc->select, "time_rcvd", 9, 1ll);
|
|
return s_doc;
|
|
}
|
|
|
|
static struct query_doc* create_query(struct queryopt* opt)
|
|
{
|
|
struct query_doc* qu_doc;
|
|
bson_t* query_what, * order_what, * msg_what, * date_what;
|
|
struct tm tm;
|
|
time_t t;
|
|
int64_t ts;
|
|
|
|
qu_doc = malloc (sizeof (struct query_doc));
|
|
qu_doc->query = bson_new ();
|
|
query_what = bson_new ();
|
|
bson_init (query_what);
|
|
bson_append_document_begin (qu_doc->query, "$query", 6, query_what);
|
|
if (opt->bsever == 1)
|
|
{
|
|
bson_append_int32 (query_what, "syslog_sever", 12,
|
|
opt->e_sever);
|
|
}
|
|
if (opt->blevel == 1)
|
|
{
|
|
bson_append_utf8 (query_what, "level", 5, opt->e_level, -1);
|
|
}
|
|
|
|
if (opt->bmsg == 1)
|
|
{
|
|
msg_what = bson_new ();
|
|
bson_init (msg_what);
|
|
bson_append_document_begin (query_what, "msg", 3, msg_what);
|
|
bson_append_utf8 (msg_what, "$regex", 6, opt->e_msg, -1);
|
|
bson_append_utf8 (msg_what, "$options", 8, "i", 1);
|
|
bson_append_document_end (query_what, msg_what);
|
|
}
|
|
|
|
if (opt->bdate == 1)
|
|
{
|
|
date_what = bson_new ();
|
|
bson_init (date_what);
|
|
bson_append_document_begin (query_what, "time_rcvd", 9,
|
|
date_what);
|
|
if (opt->bdatef == 1)
|
|
{
|
|
tm.tm_isdst = -1;
|
|
strptime (opt->e_date, "%d/%m/%Y-%H:%M:%S", &tm);
|
|
tm.tm_hour = tm.tm_hour + 1;
|
|
t = mktime (&tm);
|
|
ts = 1000 * (int64_t) t;
|
|
bson_append_date_time (date_what, "$gt", 3, ts);
|
|
}
|
|
|
|
if (opt->bdateu == 1)
|
|
{
|
|
tm.tm_isdst = -1;
|
|
strptime (opt->e_dateu, "%d/%m/%Y-%H:%M:%S", &tm);
|
|
tm.tm_hour = tm.tm_hour + 1;
|
|
t = mktime (&tm);
|
|
ts = 1000 * (int64_t) t;
|
|
bson_append_date_time (date_what, "$lt", 3, ts);
|
|
}
|
|
bson_append_document_end (query_what, date_what);
|
|
}
|
|
|
|
if (opt->bsys == 1)
|
|
{
|
|
bson_append_utf8 (query_what, "sys", 3, opt->e_sys, -1);
|
|
}
|
|
|
|
bson_append_document_end (qu_doc->query, query_what);
|
|
|
|
order_what = bson_new ();
|
|
bson_init (order_what);
|
|
bson_append_document_begin (qu_doc->query, "$orderby", 8, order_what);
|
|
bson_append_date_time (order_what, "time_rcvd", 9, 1ll);
|
|
bson_append_document_end (qu_doc->query, order_what);
|
|
|
|
bson_free (order_what);
|
|
return qu_doc;
|
|
}
|
|
|
|
static struct db_connect* create_conn(void)
|
|
{
|
|
struct db_connect* db_conn;
|
|
|
|
db_conn = malloc (sizeof (struct db_connect));
|
|
db_conn->conn = mongoc_client_new ("mongodb://localhost:27017");
|
|
if (!db_conn->conn)
|
|
{
|
|
perror ("mongo_sync_connect()");
|
|
exit (1);
|
|
}
|
|
return db_conn;
|
|
}
|
|
|
|
static void close_conn(struct db_connect* db_conn)
|
|
{
|
|
mongoc_client_destroy (db_conn->conn);
|
|
free (db_conn);
|
|
}
|
|
|
|
static void free_cursor(struct db_cursor* db_c)
|
|
{
|
|
mongoc_cursor_destroy (db_c->cursor);
|
|
free (db_c);
|
|
}
|
|
|
|
static struct db_cursor* launch_query(struct queryopt* opt,
|
|
__attribute__((unused)) struct select_doc* s_doc,
|
|
struct query_doc* qu_doc,
|
|
struct db_collection* db_coll)
|
|
{
|
|
struct db_cursor* out;
|
|
#if MONGOC_CHECK_VERSION (1, 5, 0) /* Declaration before code (ISO C90) */
|
|
const bson_t* opts = BCON_NEW (
|
|
"skip", BCON_INT32 (opt->e_skip),
|
|
"limit", BCON_INT32 (opt->e_ret)
|
|
);
|
|
#endif /* MONGOC_CHECK_VERSION (1, 5, 0) */
|
|
|
|
out = malloc (sizeof (struct db_cursor));
|
|
if (!out)
|
|
{
|
|
perror ("mongo_sync_cmd_query()");
|
|
printf ("malloc failed\n");
|
|
exit (1);
|
|
}
|
|
#if MONGOC_CHECK_VERSION (1, 5, 0)
|
|
out->cursor = mongoc_collection_find_with_opts (db_coll->collection,
|
|
qu_doc->query, opts,
|
|
NULL);
|
|
#else /* !MONGOC_CHECK_VERSION (1, 5, 0) */
|
|
out->cursor = mongoc_collection_find (db_coll->collection,
|
|
MONGOC_QUERY_NONE,
|
|
(uint32_t)opt->e_skip,
|
|
(uint32_t)opt->e_ret, 0,
|
|
qu_doc->query, s_doc->select,
|
|
NULL);
|
|
#endif /* MONGOC_CHECK_VERSION (1, 5, 0) */
|
|
if (!out->cursor)
|
|
{
|
|
perror ("mongo_sync_cmd_query()");
|
|
printf ("no records found\n");
|
|
exit (1);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
static int cursor_next(struct db_cursor* db_c, struct results* res)
|
|
{
|
|
if (mongoc_cursor_next (db_c->cursor, &res->result))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
static struct db_collection* get_collection(struct db_connect* db_conn)
|
|
{
|
|
struct db_collection* coll;
|
|
|
|
coll = malloc (sizeof (struct db_collection));
|
|
coll->collection = mongoc_client_get_collection (db_conn->conn,
|
|
"syslog", "log");
|
|
return coll;
|
|
}
|
|
|
|
static void release_collection(struct db_collection* db_coll)
|
|
{
|
|
mongoc_collection_destroy (db_coll->collection);
|
|
free (db_coll);
|
|
}
|
|
|
|
int main(int argc, char* argv[])
|
|
{
|
|
|
|
struct queryopt opt;
|
|
struct ofields* fields;
|
|
struct select_doc* s_doc;
|
|
struct query_doc* qu_doc;
|
|
struct db_connect* db_conn;
|
|
struct db_cursor* db_c;
|
|
struct db_collection* db_coll;
|
|
struct results* res;
|
|
|
|
memset (&opt, 0, sizeof (struct queryopt));
|
|
|
|
mongoc_init (); /* Initialisation of mongo-c-driver */
|
|
|
|
getoptions (argc, argv, &opt);
|
|
qu_doc = create_query (&opt); /* create query */
|
|
s_doc = create_select ();
|
|
db_conn = create_conn (); /* create connection */
|
|
db_coll = get_collection (db_conn); /* Get the collection to perform query on */
|
|
db_c = launch_query (&opt, s_doc, qu_doc, db_coll); /* launch the query and get the related cursor */
|
|
|
|
res = malloc (sizeof (struct results));
|
|
while (cursor_next (db_c, res)) /* Move cursor & get pointed data */
|
|
{
|
|
fields = get_data (res);
|
|
formater (fields); /* format output */
|
|
free (fields);
|
|
}
|
|
|
|
free (res);
|
|
free_cursor (db_c);
|
|
release_collection (db_coll);
|
|
close_conn (db_conn);
|
|
free (s_doc);
|
|
free (qu_doc);
|
|
|
|
mongoc_cleanup (); /* Cleanup of mongo-c-driver */
|
|
|
|
return (0);
|
|
}
|