1 /*
2  * Copyright (c) 2002-2013 Balabit
3  * Copyright (c) 1998-2013 Balázs Scheidler
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 as published
7  * by the Free Software Foundation, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17  *
18  * As an additional exemption you are allowed to compile & link against the
19  * OpenSSL libraries as published by the OpenSSL project. See the file
20  * COPYING for details.
21  *
22  */
23 
24 #include "pseudofile.h"
25 #include "messages.h"
26 #include "scratch-buffers.h"
27 
28 #include <string.h>
29 #include <fcntl.h>
30 #include <errno.h>
31 #include <unistd.h>
32 
33 typedef struct _PseudoFileDestDriver
34 {
35   LogDestDriver super;
36   LogTemplateOptions template_options;
37   LogTemplate *template;
38   gchar *pseudofile_name;
39   time_t suspend_until;
40   time_t time_reopen;
41 } PseudoFileDestDriver;
42 
43 /*
44  * Locking
45  * =======
46  *
47  * The pseudofile driver currently has a single, global lock that protects
48  * writing the data into the procfile.
49  *
50  * We could use a per-destination lock, however in that case nothing would
51  * prevent races when two pseudofile drivers open the same proc file.
52  *
53  * Also this driver shouldn't be that performance intensive (e.g.  pushing
54  * data to a procfile more than a dozen times per second is really sick),
55  * I've just measured pseudofile to be able to do 25-26k/sec easily in my
56  * debug build.
57  */
58 G_LOCK_DEFINE_STATIC(pseudofile_lock);
59 
60 LogTemplateOptions *
pseudofile_dd_get_template_options(LogDriver * s)61 pseudofile_dd_get_template_options(LogDriver *s)
62 {
63   PseudoFileDestDriver *self = (PseudoFileDestDriver *) s;
64 
65   return &self->template_options;
66 }
67 
68 void
pseudofile_dd_set_template(LogDriver * s,LogTemplate * template)69 pseudofile_dd_set_template(LogDriver *s, LogTemplate *template)
70 {
71   PseudoFileDestDriver *self = (PseudoFileDestDriver *) s;
72 
73   log_template_unref(self->template);
74   self->template = template;
75 }
76 
77 void
pseudofile_dd_set_time_reopen(LogDriver * s,time_t time_reopen)78 pseudofile_dd_set_time_reopen(LogDriver *s, time_t time_reopen)
79 {
80   PseudoFileDestDriver *self = (PseudoFileDestDriver *) s;
81 
82   self->time_reopen = time_reopen;
83 }
84 
85 static void
_format_message(PseudoFileDestDriver * self,LogMessage * msg,GString * formatted_message)86 _format_message(PseudoFileDestDriver *self, LogMessage *msg, GString *formatted_message)
87 {
88   LogTemplateEvalOptions options = {&self->template_options, LTZ_LOCAL, 0, NULL};
89   log_template_format(self->template, msg, &options, formatted_message);
90 }
91 
92 static EVTTAG *
_evt_tag_message(const GString * msg)93 _evt_tag_message(const GString *msg)
94 {
95   const int max_len = 30;
96 
97   return evt_tag_printf("message", "%.*s%s",
98                         (int) MIN(max_len, msg->len), msg->str,
99                         msg->len > max_len ? "..." : "");
100 }
101 
102 static gboolean
_write_message(PseudoFileDestDriver * self,const GString * msg)103 _write_message(PseudoFileDestDriver *self, const GString *msg)
104 {
105   int fd;
106   gboolean success = FALSE;
107   gint rc;
108 
109   msg_debug("Posting message to pseudo file",
110             evt_tag_str("pseudofile", self->pseudofile_name),
111             evt_tag_str("driver", self->super.super.id),
112             _evt_tag_message(msg));
113   fd = open(self->pseudofile_name, O_NOCTTY | O_WRONLY | O_NONBLOCK);
114   if (fd < 0)
115     {
116       msg_error("Error opening pseudo file",
117                 evt_tag_str("pseudofile", self->pseudofile_name),
118                 evt_tag_str("driver", self->super.super.id),
119                 evt_tag_error("error"),
120                 _evt_tag_message(msg));
121       goto exit;
122     }
123 
124   rc = write(fd, msg->str, msg->len);
125   if (rc < 0)
126     {
127       msg_error("Error writing to pseudo file",
128                 evt_tag_str("pseudofile", self->pseudofile_name),
129                 evt_tag_str("driver", self->super.super.id),
130                 evt_tag_error("error"),
131                 _evt_tag_message(msg));
132       goto exit;
133     }
134   else if (rc != msg->len)
135     {
136       msg_error("Partial write to pseudofile, probably the output is too much for the kernel to consume",
137                 evt_tag_str("pseudofile", self->pseudofile_name),
138                 evt_tag_str("driver", self->super.super.id),
139                 _evt_tag_message(msg));
140       goto exit;
141     }
142 
143   success = TRUE;
144 
145 exit:
146   if (fd >= 0)
147     close(fd);
148 
149   return success;
150 }
151 
152 static gboolean
_is_output_suspended(PseudoFileDestDriver * self,time_t now)153 _is_output_suspended(PseudoFileDestDriver *self, time_t now)
154 {
155   if (self->suspend_until && self->suspend_until > now)
156     return TRUE;
157   return FALSE;
158 }
159 
160 static void
_suspend_output(PseudoFileDestDriver * self,time_t now)161 _suspend_output(PseudoFileDestDriver *self, time_t now)
162 {
163   self->suspend_until = now + self->time_reopen;
164 }
165 
166 static void
pseudofile_dd_queue(LogPipe * s,LogMessage * msg,const LogPathOptions * path_options)167 pseudofile_dd_queue(LogPipe *s, LogMessage *msg, const LogPathOptions *path_options)
168 {
169   PseudoFileDestDriver *self = (PseudoFileDestDriver *) s;
170   GString *formatted_message = scratch_buffers_alloc();
171   gboolean success;
172   time_t now = msg->timestamps[LM_TS_RECVD].ut_sec;
173 
174   if (_is_output_suspended(self, now))
175     goto finish;
176 
177   _format_message(self, msg, formatted_message);
178 
179   G_LOCK(pseudofile_lock);
180   success = _write_message(self, formatted_message);
181   G_UNLOCK(pseudofile_lock);
182 
183   if (!success)
184     _suspend_output(self, now);
185 
186 finish:
187   log_dest_driver_queue_method(s, msg, path_options);
188 }
189 
190 static gboolean
pseudofile_dd_init(LogPipe * s)191 pseudofile_dd_init(LogPipe *s)
192 {
193   PseudoFileDestDriver *self = (PseudoFileDestDriver *) s;
194   GlobalConfig *cfg = log_pipe_get_config(s);
195 
196   log_template_options_init(&self->template_options, cfg);
197 
198   if (self->time_reopen == -1)
199     self->time_reopen = cfg->time_reopen;
200 
201   if (!self->template)
202     {
203       msg_error("The template() option for pseudofile() is mandatory", log_pipe_location_tag(s));
204       return FALSE;
205     }
206 
207   return log_dest_driver_init_method(s);
208 }
209 
210 static void
pseudofile_dd_free(LogPipe * s)211 pseudofile_dd_free(LogPipe *s)
212 {
213   PseudoFileDestDriver *self = (PseudoFileDestDriver *) s;
214 
215   log_template_options_destroy(&self->template_options);
216   g_free(self->pseudofile_name);
217   log_template_unref(self->template);
218   log_dest_driver_free(s);
219 }
220 
221 LogDriver *
pseudofile_dd_new(gchar * pseudofile_name,GlobalConfig * cfg)222 pseudofile_dd_new(gchar *pseudofile_name, GlobalConfig *cfg)
223 {
224   PseudoFileDestDriver *self = g_new0(PseudoFileDestDriver, 1);
225 
226   log_dest_driver_init_instance(&self->super, cfg);
227   log_template_options_defaults(&self->template_options);
228   self->super.super.super.init = pseudofile_dd_init;
229   self->super.super.super.queue = pseudofile_dd_queue;
230   self->super.super.super.free_fn = pseudofile_dd_free;
231   self->pseudofile_name = g_strdup(pseudofile_name);
232   self->time_reopen = -1;
233   return &self->super.super;
234 }
235