mirror of
https://github.com/rsyslog/rsyslog.git
synced 2025-12-17 10:30:43 +01:00
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 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>
|
|
|
|
/* we need this to avoid issues with older versions of libbson */
|
|
#ifdef __GNUC__
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wpragmas"
|
|
#pragma GCC diagnostic ignored "-Wunknown-attributes"
|
|
#pragma GCC diagnostic ignored "-Wexpansion-to-defined"
|
|
#endif
|
|
#include <mongoc.h>
|
|
#include <bson.h>
|
|
#ifdef __GNUC__
|
|
#pragma GCC diagnostic pop
|
|
#endif
|
|
|
|
#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);
|
|
}
|