summaryrefslogtreecommitdiffstats
path: root/tools/logctl.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/logctl.c')
-rw-r--r--tools/logctl.c486
1 files changed, 486 insertions, 0 deletions
diff --git a/tools/logctl.c b/tools/logctl.c
new file mode 100644
index 0000000..1010409
--- /dev/null
+++ b/tools/logctl.c
@@ -0,0 +1,486 @@
+/**
+ * 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>
+
+/* 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"
+#pragma GCC diagnostic ignored "-Wstrict-prototypes"
+#pragma GCC diagnostic ignored "-Wold-style-definition"
+#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);
+}