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