1 /**
2 * logctl - a tool to access lumberjack logs in MongoDB
3 * ... and potentially other sources in the future.
4 *
5 * Copyright 2012 Ulrike Gerhards and Adiscon GmbH.
6 *
7 * Copyright 2017 Hugo Soszynski and aDvens
8 *
9 * long short
10
11 * level l read records with level x
12 * severity s read records with severity x
13 * ret r number of records to return
14 * skip k number of records to skip
15 * sys y read records of system x
16 * msg m read records with message containing x
17 * datef f read records starting on time received x
18 * dateu u read records until time received x
19 *
20 * examples:
21 *
22 * logctl -f 15/05/2012-12:00:00 -u 15/05/2012-12:37:00
23 * logctl -s 50 --ret 10
24 * logctl -m "closed"
25 * logctl -l "INFO"
26 * logctl -s 3
27 * logctl -y "ubuntu"
28 *
29 * This file is part of rsyslog.
30 *
31 * Licensed under the Apache License, Version 2.0 (the "License");
32 * you may not use this file except in compliance with the License.
33 * You may obtain a copy of the License at
34 *
35 * http://www.apache.org/licenses/LICENSE-2.0
36 * -or-
37 * see COPYING.ASL20 in the source distribution
38 *
39 * Unless required by applicable law or agreed to in writing, software
40 * distributed under the License is distributed on an "AS IS" BASIS,
41 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
42 * See the License for the specific language governing permissions and
43 * limitations under the License.
44 */
45 #include "config.h"
46 #define _XOPEN_SOURCE 700 /* Need to define POSIX version to use strptime() */
47 #include <stdio.h>
48 #include <errno.h>
49 #include <string.h>
50 #include <stdlib.h>
51 #include <time.h>
52 #include <getopt.h>
53 #include <unistd.h>
54
55 /* we need this to avoid issues with older versions of libbson */
56 #ifdef __GNUC__
57 #pragma GCC diagnostic push
58 #pragma GCC diagnostic ignored "-Wpragmas"
59 #pragma GCC diagnostic ignored "-Wunknown-attributes"
60 #pragma GCC diagnostic ignored "-Wexpansion-to-defined"
61 #endif
62 #include <mongoc.h>
63 #include <bson.h>
64 #ifdef __GNUC__
65 #pragma GCC diagnostic pop
66 #endif
67
68 #define N 80
69
70 static struct option long_options[] =
71 {
72 {"level", required_argument, NULL, 'l'},
73 {"severity", required_argument, NULL, 's'},
74 {"ret", required_argument, NULL, 'r'},
75 {"skip", required_argument, NULL, 'k'},
76 {"sys", required_argument, NULL, 'y'},
77 {"msg", required_argument, NULL, 'm'},
78 {"datef", required_argument, NULL, 'f'},
79 {"dateu", required_argument, NULL, 'u'},
80 {NULL, 0, NULL, 0}
81 };
82
83 struct queryopt
84 {
85 int32_t e_sever;
86 int32_t e_ret;
87 int32_t e_skip;
88 char* e_date;
89 char* e_level;
90 char* e_msg;
91 char* e_sys;
92 char* e_dateu;
93 int bsever;
94 int blevel;
95 int bskip;
96 int bret;
97 int bsys;
98 int bmsg;
99 int bdate;
100 int bdatef;
101 int bdateu;
102 };
103
104 struct ofields
105 {
106 const char* msg;
107 const char* syslog_tag;
108 const char* prog;
109 char* date;
110 int64_t date_r;
111 };
112
113 struct query_doc
114 {
115 bson_t* query;
116 };
117
118 struct select_doc
119 {
120 bson_t* select;
121 };
122
123 struct db_connect
124 {
125 mongoc_client_t* conn;
126 };
127
128 struct db_collection
129 {
130 mongoc_collection_t* collection;
131 };
132
133 struct db_cursor
134 {
135 mongoc_cursor_t* cursor;
136 };
137
138 struct results
139 {
140 const bson_t* result;
141 };
142
143
formater(struct ofields * fields)144 static void formater(struct ofields* fields)
145 {
146 char str[N];
147 time_t rtime;
148 struct tm now;
149
150 rtime = (time_t) (fields->date_r / 1000);
151 strftime (str, N, "%b %d %H:%M:%S", gmtime_r (&rtime, &now));
152 printf ("%s %s %s %s\n", str, fields->prog, fields->syslog_tag,
153 fields->msg);
154 }
155
get_data(struct results * res)156 static struct ofields* get_data(struct results* res)
157 {
158 struct ofields* fields;
159 const char* msg;
160 const char* prog;
161 const char* syslog_tag;
162 int64_t date_r;
163 bson_iter_t c;
164
165 fields = malloc (sizeof (struct ofields));
166 bson_iter_init_find (&c, res->result, "msg");
167 if (!(msg = bson_iter_utf8 (&c, NULL)))
168 {
169 perror ("bson_cursor_get_string()");
170 exit (1);
171 }
172
173 bson_iter_init_find (&c, res->result, "sys");
174 if (!(prog = bson_iter_utf8 (&c, NULL)))
175 {
176 perror ("bson_cursor_get_string()");
177 exit (1);
178 }
179
180 bson_iter_init_find (&c, res->result, "syslog_tag");
181 if (!(syslog_tag = bson_iter_utf8 (&c, NULL)))
182 {
183 perror ("bson_cursor_get_string()");
184 exit (1);
185 }
186
187 bson_iter_init_find (&c, res->result, "time_rcvd");
188 if (!(date_r = bson_iter_date_time (&c)))
189 {
190 perror ("bson_cursor_get_utc_datetime()");
191 exit (1);
192 }
193
194 fields->msg = msg;
195 fields->prog = prog;
196 fields->syslog_tag = syslog_tag;
197 fields->date_r = date_r;
198
199 return fields;
200 }
201
getoptions(int argc,char * argv[],struct queryopt * opt)202 static void getoptions(int argc, char* argv[], struct queryopt* opt)
203 {
204 int iarg;
205
206 while ((iarg = getopt_long (argc, argv, "l:s:r:k:y:f:u:m:",
207 long_options, NULL)) != -1)
208 {
209 /* check to see if a single character or long option came through */
210 switch (iarg)
211 {
212 /* short option 's' */
213 case 's':
214 opt->bsever = 1;
215 opt->e_sever = atoi (optarg);
216 break;
217 /* short option 'r' */
218 case 'r':
219 opt->bret = 1;
220 opt->e_ret = atoi (optarg);
221 break;
222 /* short option 'f' : date from */
223 case 'f':
224 opt->bdate = 1;
225 opt->bdatef = 1;
226 opt->e_date = optarg;
227 break;
228 /* short option 'u': date until */
229 case 'u':
230 opt->bdate = 1;
231 opt->bdateu = 1;
232 opt->e_dateu = optarg;
233 break;
234 /* short option 'k' */
235 case 'k':
236 opt->bskip = 1;
237 opt->e_skip = atoi (optarg);
238 break;
239 /* short option 'l' */
240 case 'l':
241 opt->blevel = 1;
242 opt->e_level = optarg;
243 break;
244 /* short option 'm' */
245 case 'm':
246 opt->bmsg = 1;
247 opt->e_msg = optarg;
248 break;
249 /* short option 'y' */
250 case 'y':
251 opt->bsys = 1;
252 opt->e_sys = optarg;
253 break;
254 default:
255 break;
256 } /* end switch iarg */
257 } /* end while */
258
259 } /* end void getoptions */
260
create_select(void)261 static struct select_doc* create_select(void)
262 /* BSON object indicating the fields to return */
263 {
264 struct select_doc* s_doc;
265
266 s_doc = malloc (sizeof (struct select_doc));
267 s_doc->select = bson_new ();
268 bson_append_utf8 (s_doc->select, "syslog_tag", 10, "s", 1);
269 bson_append_utf8 (s_doc->select, "msg", 3, "ERROR", 5);
270 bson_append_utf8 (s_doc->select, "sys", 3, "sys", 3);
271 bson_append_date_time (s_doc->select, "time_rcvd", 9, 1ll);
272 return s_doc;
273 }
274
create_query(struct queryopt * opt)275 static struct query_doc* create_query(struct queryopt* opt)
276 {
277 struct query_doc* qu_doc;
278 bson_t* query_what, * order_what, * msg_what, * date_what;
279 struct tm tm;
280 time_t t;
281 int64_t ts;
282
283 qu_doc = malloc (sizeof (struct query_doc));
284 qu_doc->query = bson_new ();
285 query_what = bson_new ();
286 bson_init (query_what);
287 bson_append_document_begin (qu_doc->query, "$query", 6, query_what);
288 if (opt->bsever == 1)
289 {
290 bson_append_int32 (query_what, "syslog_sever", 12,
291 opt->e_sever);
292 }
293 if (opt->blevel == 1)
294 {
295 bson_append_utf8 (query_what, "level", 5, opt->e_level, -1);
296 }
297
298 if (opt->bmsg == 1)
299 {
300 msg_what = bson_new ();
301 bson_init (msg_what);
302 bson_append_document_begin (query_what, "msg", 3, msg_what);
303 bson_append_utf8 (msg_what, "$regex", 6, opt->e_msg, -1);
304 bson_append_utf8 (msg_what, "$options", 8, "i", 1);
305 bson_append_document_end (query_what, msg_what);
306 }
307
308 if (opt->bdate == 1)
309 {
310 date_what = bson_new ();
311 bson_init (date_what);
312 bson_append_document_begin (query_what, "time_rcvd", 9,
313 date_what);
314 if (opt->bdatef == 1)
315 {
316 tm.tm_isdst = -1;
317 strptime (opt->e_date, "%d/%m/%Y-%H:%M:%S", &tm);
318 tm.tm_hour = tm.tm_hour + 1;
319 t = mktime (&tm);
320 ts = 1000 * (int64_t) t;
321 bson_append_date_time (date_what, "$gt", 3, ts);
322 }
323
324 if (opt->bdateu == 1)
325 {
326 tm.tm_isdst = -1;
327 strptime (opt->e_dateu, "%d/%m/%Y-%H:%M:%S", &tm);
328 tm.tm_hour = tm.tm_hour + 1;
329 t = mktime (&tm);
330 ts = 1000 * (int64_t) t;
331 bson_append_date_time (date_what, "$lt", 3, ts);
332 }
333 bson_append_document_end (query_what, date_what);
334 }
335
336 if (opt->bsys == 1)
337 {
338 bson_append_utf8 (query_what, "sys", 3, opt->e_sys, -1);
339 }
340
341 bson_append_document_end (qu_doc->query, query_what);
342
343 order_what = bson_new ();
344 bson_init (order_what);
345 bson_append_document_begin (qu_doc->query, "$orderby", 8, order_what);
346 bson_append_date_time (order_what, "time_rcvd", 9, 1ll);
347 bson_append_document_end (qu_doc->query, order_what);
348
349 bson_free (order_what);
350 return qu_doc;
351 }
352
create_conn(void)353 static struct db_connect* create_conn(void)
354 {
355 struct db_connect* db_conn;
356
357 db_conn = malloc (sizeof (struct db_connect));
358 db_conn->conn = mongoc_client_new ("mongodb://localhost:27017");
359 if (!db_conn->conn)
360 {
361 perror ("mongo_sync_connect()");
362 exit (1);
363 }
364 return db_conn;
365 }
366
close_conn(struct db_connect * db_conn)367 static void close_conn(struct db_connect* db_conn)
368 {
369 mongoc_client_destroy (db_conn->conn);
370 free (db_conn);
371 }
372
free_cursor(struct db_cursor * db_c)373 static void free_cursor(struct db_cursor* db_c)
374 {
375 mongoc_cursor_destroy (db_c->cursor);
376 free (db_c);
377 }
378
launch_query(struct queryopt * opt,struct select_doc * s_doc,struct query_doc * qu_doc,struct db_collection * db_coll)379 static struct db_cursor* launch_query(struct queryopt* opt,
380 __attribute__((unused)) struct select_doc* s_doc,
381 struct query_doc* qu_doc,
382 struct db_collection* db_coll)
383 {
384 struct db_cursor* out;
385 #if MONGOC_CHECK_VERSION (1, 5, 0) /* Declaration before code (ISO C90) */
386 const bson_t* opts = BCON_NEW (
387 "skip", BCON_INT32 (opt->e_skip),
388 "limit", BCON_INT32 (opt->e_ret)
389 );
390 #endif /* MONGOC_CHECK_VERSION (1, 5, 0) */
391
392 out = malloc (sizeof (struct db_cursor));
393 if (!out)
394 {
395 perror ("mongo_sync_cmd_query()");
396 printf ("malloc failed\n");
397 exit (1);
398 }
399 #if MONGOC_CHECK_VERSION (1, 5, 0)
400 out->cursor = mongoc_collection_find_with_opts (db_coll->collection,
401 qu_doc->query, opts,
402 NULL);
403 #else /* !MONGOC_CHECK_VERSION (1, 5, 0) */
404 out->cursor = mongoc_collection_find (db_coll->collection,
405 MONGOC_QUERY_NONE,
406 (uint32_t)opt->e_skip,
407 (uint32_t)opt->e_ret, 0,
408 qu_doc->query, s_doc->select,
409 NULL);
410 #endif /* MONGOC_CHECK_VERSION (1, 5, 0) */
411 if (!out->cursor)
412 {
413 perror ("mongo_sync_cmd_query()");
414 printf ("no records found\n");
415 exit (1);
416 }
417 return out;
418 }
419
cursor_next(struct db_cursor * db_c,struct results * res)420 static int cursor_next(struct db_cursor* db_c, struct results* res)
421 {
422 if (mongoc_cursor_next (db_c->cursor, &res->result))
423 return true;
424 return false;
425 }
426
get_collection(struct db_connect * db_conn)427 static struct db_collection* get_collection(struct db_connect* db_conn)
428 {
429 struct db_collection* coll;
430
431 coll = malloc (sizeof (struct db_collection));
432 coll->collection = mongoc_client_get_collection (db_conn->conn,
433 "syslog", "log");
434 return coll;
435 }
436
release_collection(struct db_collection * db_coll)437 static void release_collection(struct db_collection* db_coll)
438 {
439 mongoc_collection_destroy (db_coll->collection);
440 free (db_coll);
441 }
442
main(int argc,char * argv[])443 int main(int argc, char* argv[])
444 {
445
446 struct queryopt opt;
447 struct ofields* fields;
448 struct select_doc* s_doc;
449 struct query_doc* qu_doc;
450 struct db_connect* db_conn;
451 struct db_cursor* db_c;
452 struct db_collection* db_coll;
453 struct results* res;
454
455 memset (&opt, 0, sizeof (struct queryopt));
456
457 mongoc_init (); /* Initialisation of mongo-c-driver */
458
459 getoptions (argc, argv, &opt);
460 qu_doc = create_query (&opt); /* create query */
461 s_doc = create_select ();
462 db_conn = create_conn (); /* create connection */
463 db_coll = get_collection (db_conn); /* Get the collection to perform query on */
464 db_c = launch_query (&opt, s_doc, qu_doc, db_coll); /* launch the query and get the related cursor */
465
466 res = malloc (sizeof (struct results));
467 while (cursor_next (db_c, res)) /* Move cursor & get pointed data */
468 {
469 fields = get_data (res);
470 formater (fields); /* format output */
471 free (fields);
472 }
473
474 free (res);
475 free_cursor (db_c);
476 release_collection (db_coll);
477 close_conn (db_conn);
478 free (s_doc);
479 free (qu_doc);
480
481 mongoc_cleanup (); /* Cleanup of mongo-c-driver */
482
483 return (0);
484 }
485