1 /*
2  * Copyright (c) 2017 Balabit
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License version 2 as published
6  * by the Free Software Foundation, or (at your option) any later version.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
16  *
17  * As an additional exemption you are allowed to compile & link against the
18  * OpenSSL libraries as published by the OpenSSL project. See the file
19  * COPYING for details.
20  *
21  */
22 
23 #include "plugin.h"
24 #include "plugin-types.h"
25 #include "template/simple-function.h"
26 #include "messages.h"
27 #include "cfg.h"
28 
29 #include "syslog-ng-config.h"
30 
31 #include <stdlib.h>
32 #include <math.h>
33 
34 static gboolean
_is_leap_year(int year)35 _is_leap_year(int year)
36 {
37   return ((year%4==0) && (year % 100!=0)) || (year%400==0);
38 }
39 
40 static time_t
year_in_seconds(int year)41 year_in_seconds(int year)
42 {
43   if (_is_leap_year(year))
44     return 31622400;
45   else
46     return 31536000;
47 }
48 
49 typedef struct
50 {
51   TFSimpleFuncState super;
52   int precision;
53 } TFStardateState;
54 
55 static guint64 power10[10] = { 1, 10, 100, 1000, 10000,
56                                100000, 1000000, 10000000, 100000000, 1000000000
57                              };
58 
59 gboolean
tf_stardate_prepare(LogTemplateFunction * self,gpointer s,LogTemplate * parent,gint argc,gchar * argv[],GError ** error)60 tf_stardate_prepare(LogTemplateFunction *self, gpointer s, LogTemplate *parent,
61                     gint argc, gchar *argv[], GError **error)
62 {
63   TFStardateState *state = (TFStardateState *) s;
64   state->precision = 2;
65 
66   GOptionEntry stardate_options[] =
67   {
68     { "digits", 'd', 0, G_OPTION_ARG_INT, &state->precision, "Precision: 0-9. Default: 2.", NULL },
69     { NULL }
70   };
71 
72   GOptionContext *ctx = g_option_context_new("stardate");
73   g_option_context_add_main_entries(ctx, stardate_options, NULL);
74 
75   if (!g_option_context_parse(ctx, &argc, &argv, error))
76     {
77       g_option_context_free(ctx);
78       return FALSE;
79     }
80   g_option_context_free(ctx);
81 
82   if (state->precision < 0 || state->precision > 9)
83     {
84       g_set_error(error, LOG_TEMPLATE_ERROR, LOG_TEMPLATE_ERROR_COMPILE,
85                   "stardate: digits must be between 0-9.\n");
86       return FALSE;
87     }
88 
89   if (argc != 2)
90     {
91       g_set_error(error, LOG_TEMPLATE_ERROR, LOG_TEMPLATE_ERROR_COMPILE,
92                   "stardate: format must be: $(stardate [--digits 2] $UNIXTIME)\n");
93       return FALSE;
94     }
95 
96   if (!tf_simple_func_prepare(self, state, parent, argc, argv, error))
97     {
98       g_set_error(error, LOG_TEMPLATE_ERROR, LOG_TEMPLATE_ERROR_COMPILE,
99                   "stardate: stardate: prepare failed");
100       return FALSE;
101     }
102 
103   return TRUE;
104 }
105 
106 static void
tf_stardate_call(LogTemplateFunction * self,gpointer s,const LogTemplateInvokeArgs * args,GString * result)107 tf_stardate_call(LogTemplateFunction *self, gpointer s,
108                  const LogTemplateInvokeArgs *args, GString *result)
109 {
110   TFStardateState  *state = (TFStardateState *) s;
111 
112   char *status;
113   time_t time_to_convert = strtol(args->argv[0]->str, &status, 10);
114   if (*status)
115     {
116       msg_error("stardate: wrong format: expected unixtime like value",
117                 evt_tag_str("got:", args->argv[0]->str));
118       return;
119     }
120 
121   struct tm tm_time;
122   localtime_r(&time_to_convert, &tm_time );
123 
124   struct tm secs_beginning_year = {.tm_year = tm_time.tm_year, .tm_mday = 1};
125   time_t elapsed_seconds = time_to_convert - mktime(&secs_beginning_year);
126 
127   double fraction = (double)elapsed_seconds/year_in_seconds(tm_time.tm_year);
128   double truncated = (double) floor(fraction * power10[state->precision]) / power10[state->precision];
129 
130   g_string_append_printf(result, "%0.*lf", state->precision, 1900 + tm_time.tm_year + truncated);
131 }
132 
133 TEMPLATE_FUNCTION(TFStardateState, tf_stardate, tf_stardate_prepare,
134                   tf_simple_func_eval, tf_stardate_call, tf_simple_func_free_state, NULL);
135