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