1 /****************************************************************************
2 *
3 * Copyright (C) 2003-2009 Sourcefire, Inc.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License Version 2 as
7 * published by the Free Software Foundation. You may not use, modify or
8 * distribute this program under any other version of the GNU General
9 * Public License.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 *
20 ****************************************************************************/
21
22 /**
23 * @file sf_textlog.c
24 * @author Russ Combs <rcombs@sourcefire.com>
25 * @date
26 *
27 * @brief implements buffered text stream for logging
28 */
29
30 #include <stdarg.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <sys/stat.h>
35
36 #include "sf_textlog.h"
37 #include "log.h"
38 #include "util.h"
39
40 /* some reasonable minimums */
41 #define MIN_BUF (1*K_BYTES)
42 #define MIN_FILE (MIN_BUF)
43
44 /*-------------------------------------------------------------------
45 * TextLog_Open/Close: open/close associated log file
46 *-------------------------------------------------------------------
47 */
TextLog_Open(const char * name)48 static FILE* TextLog_Open (const char* name)
49 {
50 if ( !name ) return OpenAlertFile(NULL);
51 if ( !strcasecmp(name, "stdout") ) return stdout;
52 return OpenAlertFile(name);
53 }
54
TextLog_Close(FILE * file)55 static void TextLog_Close (FILE* file)
56 {
57 if ( !file ) return;
58 if ( file != stdout ) fclose(file);
59 }
60
TextLog_Size(FILE * file)61 static size_t TextLog_Size (FILE* file)
62 {
63 struct stat sbuf;
64 int fd = fileno(file);
65 int err = fstat(fd, &sbuf);
66 return err ? 0 : sbuf.st_size;
67 }
68
69 /*-------------------------------------------------------------------
70 * TextLog_Init: constructor
71 *-------------------------------------------------------------------
72 */
TextLog_Init(const char * name,unsigned int maxBuf,size_t maxFile)73 TextLog* TextLog_Init (
74 const char* name, unsigned int maxBuf, size_t maxFile
75 ) {
76 TextLog* this;
77
78 if ( maxBuf < MIN_BUF ) maxBuf = MIN_BUF;
79 if ( maxFile < MIN_FILE ) maxFile = MIN_FILE;
80 if ( maxFile < maxBuf ) maxFile = maxBuf;
81
82 this = (TextLog*)malloc(sizeof(TextLog)+maxBuf);
83
84 if ( !this )
85 {
86 FatalError("Unable to allocate a TextLog(%u)!\n", maxBuf);
87 }
88 this->name = name ? SnortStrdup(name) : NULL;
89 this->file = TextLog_Open(this->name);
90 this->size = TextLog_Size(this->file);
91 this->last = time(NULL);
92 this->maxFile = maxFile;
93
94 this->maxBuf = maxBuf;
95 TextLog_Reset(this);
96
97 return this;
98 }
99
100 /*-------------------------------------------------------------------
101 * TextLog_Term: destructor
102 *-------------------------------------------------------------------
103 */
TextLog_Term(TextLog * this)104 void TextLog_Term (TextLog* this)
105 {
106 if ( !this ) return;
107
108 TextLog_Flush(this);
109 TextLog_Close(this->file);
110
111 if ( this->name ) free(this->name);
112 free(this);
113 }
114
115 /*-------------------------------------------------------------------
116 * TextLog_Flush: start writing to new file
117 * but don't roll over stdout or any sooner
118 * than resolution of filename discriminator
119 *-------------------------------------------------------------------
120 */
TextLog_Roll(TextLog * this)121 static void TextLog_Roll (TextLog* this)
122 {
123 if ( this->file == stdout ) return;
124 if ( this->last >= time(NULL) ) return;
125
126 TextLog_Close(this->file);
127 RollAlertFile(this->name);
128 this->file = TextLog_Open(this->name);
129
130 this->last = time(NULL);
131 this->size = 0;
132 }
133
134 /*-------------------------------------------------------------------
135 * TextLog_Flush: write buffered stream to file
136 *-------------------------------------------------------------------
137 */
TextLog_Flush(TextLog * this)138 bool TextLog_Flush(TextLog* this)
139 {
140 int ok;
141
142 if ( !this->pos ) return FALSE;
143 if ( this->size + this->pos > this->maxFile ) TextLog_Roll(this);
144
145 ok = fwrite(this->buf, this->pos, 1, this->file);
146
147 /* on stdout flush after printing to avoid lags in output */
148 if (this->file == stdout ) fflush (this->file) ;
149
150 if ( ok == 1 )
151 {
152 this->size += this->pos;
153 TextLog_Reset(this);
154 return TRUE;
155 }
156 return FALSE;
157 }
158
159 /*-------------------------------------------------------------------
160 * TextLog_Putc: append char to buffer
161 *-------------------------------------------------------------------
162 */
TextLog_Putc(TextLog * this,char c)163 bool TextLog_Putc (TextLog* this, char c)
164 {
165 if ( TextLog_Avail(this) < 1 )
166 {
167 TextLog_Flush(this);
168 }
169 this->buf[this->pos++] = c;
170 this->buf[this->pos] = '\0';
171
172 return TRUE;
173 }
174
175 /*-------------------------------------------------------------------
176 * TextLog_Write: append string to buffer
177 *-------------------------------------------------------------------
178 */
TextLog_Write(TextLog * this,const char * str,int len)179 bool TextLog_Write (TextLog* this, const char* str, int len)
180 {
181 int avail = TextLog_Avail(this);
182
183 if ( len >= avail )
184 {
185 TextLog_Flush(this);
186 avail = TextLog_Avail(this);
187 }
188 len = snprintf(this->buf+this->pos, avail, "%s", str);
189
190 if ( len >= avail )
191 {
192 this->pos = this->maxBuf - 1;
193 this->buf[this->pos] = '\0';
194 return FALSE;
195 }
196 else if ( len < 0 )
197 {
198 return FALSE;
199 }
200 this->pos += len;
201 return TRUE;
202 }
203
204 /*-------------------------------------------------------------------
205 * TextLog_Printf: append formatted string to buffer
206 *-------------------------------------------------------------------
207 */
TextLog_Print(TextLog * this,const char * fmt,...)208 bool TextLog_Print (TextLog* this, const char* fmt, ...)
209 {
210 int avail = TextLog_Avail(this);
211 int len;
212 va_list ap;
213
214 va_start(ap, fmt);
215 len = vsnprintf(this->buf+this->pos, avail, fmt, ap);
216 va_end(ap);
217
218 if ( len >= avail )
219 {
220 TextLog_Flush(this);
221 avail = TextLog_Avail(this);
222
223 va_start(ap, fmt);
224 len = vsnprintf(this->buf+this->pos, avail, fmt, ap);
225 va_end(ap);
226 }
227 if ( len >= avail )
228 {
229 this->pos = this->maxBuf - 1;
230 this->buf[this->pos] = '\0';
231 return FALSE;
232 }
233 else if ( len < 0 )
234 {
235 return FALSE;
236 }
237 this->pos += len;
238 return TRUE;
239 }
240
241 /*-------------------------------------------------------------------
242 * TextLog_Quote: write string escaping quotes
243 * FIXTHIS could be smarter by counting required escapes instead of
244 * checking for 3
245 *-------------------------------------------------------------------
246 */
TextLog_Quote(TextLog * this,const char * qs)247 bool TextLog_Quote (TextLog* this, const char* qs)
248 {
249 int pos = this->pos;
250
251 if ( TextLog_Avail(this) < 3 )
252 {
253 TextLog_Flush(this);
254 }
255 this->buf[pos++] = '"';
256
257 while ( *qs && (this->maxBuf - pos > 2) )
258 {
259 if ( *qs == '"' || *qs == '\\' )
260 {
261 this->buf[pos++] = '\\';
262 }
263 this->buf[pos++] = *qs++;
264 }
265 if ( *qs ) return FALSE;
266
267 this->buf[pos++] = '"';
268 this->pos = pos;
269
270 return TRUE;
271 }
272
273